Skip to content

Commit 2fba849

Browse files
feat: updated payloadId implementation
1 parent e4eacc5 commit 2fba849

File tree

14 files changed

+395
-92
lines changed

14 files changed

+395
-92
lines changed

FunctionSignatures.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,7 @@
554554
| `messageTransmitter` | `0x7b04c181` |
555555
| `owner` | `0x8da5cb5b` |
556556
| `ownershipHandoverExpiresAt` | `0xfee81cf4` |
557-
| `processTrigger` | `0x7f3352bc` |
557+
| `processPayload` | `0x7f3352bc` |
558558
| `proveRemoteExecutions` | `0x893289f8` |
559559
| `registerSwitchboard` | `0x74f5b1fc` |
560560
| `remoteExecutedDigests` | `0xecbf77d9` |
@@ -583,7 +583,7 @@
583583
| `isAttested` | `0xc13c2396` |
584584
| `owner` | `0x8da5cb5b` |
585585
| `ownershipHandoverExpiresAt` | `0xfee81cf4` |
586-
| `processTrigger` | `0x7f3352bc` |
586+
| `processPayload` | `0x7f3352bc` |
587587
| `registerSwitchboard` | `0x74f5b1fc` |
588588
| `renounceOwnership` | `0x715018a6` |
589589
| `requestOwnershipHandover` | `0x25692962` |
@@ -610,7 +610,7 @@
610610
| `owner` | `0x8da5cb5b` |
611611
| `ownershipHandoverExpiresAt` | `0xfee81cf4` |
612612
| `payloadCounter` | `0x550ce1d5` |
613-
| `processTrigger` | `0x7f3352bc` |
613+
| `processPayload` | `0x7f3352bc` |
614614
| `registerSibling` | `0x4f58b88c` |
615615
| `registerSwitchboard` | `0x74f5b1fc` |
616616
| `renounceOwnership` | `0x715018a6` |

PAYLOAD_ID_ARCHITECTURE.md

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
# Payload ID Architecture - Unified Design
2+
3+
## Overview
4+
Unified payload ID structure for all three payload types: Write, Trigger, and Message.
5+
6+
## Payload ID Structure
7+
8+
### Bit Layout (256 bits total)
9+
```
10+
[Origin: 64 bits][Verification: 64 bits][Pointer: 64 bits][Reserved: 64 bits]
11+
```
12+
13+
Each component breakdown:
14+
- **Origin (64 bits)**: `chainSlug (32 bits) | switchboardId/watcherId (32 bits)`
15+
- **Verification (64 bits)**: `chainSlug (32 bits) | switchboardId/watcherId (32 bits)`
16+
- **Pointer (64 bits)**: Counter value
17+
- **Reserved (64 bits)**: For future extensibility
18+
19+
## Payload Type Specifications
20+
21+
### 1. Write Payloads (EVMX → On-chain)
22+
- **Origin**: `evmxChainSlug (32) | watcherId (32)`
23+
- Generated by: Watcher (on EVMX)
24+
- Verified by: Watcher offchain (links source)
25+
- **Verification**: `dstChainSlug (32) | dstSwitchboardId (32)`
26+
- Generated by: Watcher (from config)
27+
- Used by: Socket for routing
28+
- **Pointer**: `payloadCounter (64)`
29+
- Generated by: Watcher (switchboard-specific counter)
30+
31+
**Where Created**: `Watcher.sol``getCurrentPayloadId()`
32+
33+
### 2. Trigger Payloads (On-chain → EVMX)
34+
- **Origin**: `srcChainSlug (32) | srcSwitchboardId (32)`
35+
- Generated by: FastSwitchboard
36+
- Verified by: Watcher offchain (verifies source)
37+
- **Verification**: `evmxChainSlug (32) | watcherId (32)`
38+
- Generated by: FastSwitchboard (from stored config)
39+
- Used by: Socket for routing
40+
- **Pointer**: `switchboardCounter (64)`
41+
- Generated by: FastSwitchboard (switchboard-specific counter)
42+
43+
**Where Created**: `FastSwitchboard.sol``processPayload()`
44+
45+
### 3. Message Payloads (Plug → Plug)
46+
- **Origin**: `srcChainSlug (32) | srcSwitchboardId (32)`
47+
- Generated by: MessageSwitchboard
48+
- Verified by: Destination switchboard (checks source)
49+
- **Verification**: `dstChainSlug (32) | dstSwitchboardId (32)`
50+
- Generated by: MessageSwitchboard
51+
- Used by: Socket for routing
52+
- **Pointer**: `switchboardCounter (64)`
53+
- Generated by: MessageSwitchboard (switchboard-specific counter)
54+
55+
**Where Created**: `MessageSwitchboard.sol``_createDigestAndPayloadId()`
56+
57+
## Decoding and Verification
58+
59+
### Socket Verification (Destination)
60+
1. Decode `payloadId` using `getVerificationInfo(payloadId)`
61+
2. Extract `verificationChainSlug` and `verificationSwitchboardId`
62+
3. Verify against local config:
63+
- `verificationChainSlug == local chainSlug`
64+
- `verificationSwitchboardId == local switchboardId`
65+
66+
### Source Verification (Off-chain Watcher)
67+
1. Decode `payloadId` using `getOriginInfo(payloadId)`
68+
2. Extract `originChainSlug` and `originId`
69+
3. Verify source configuration matches expected values
70+
71+
### Payload Type Detection
72+
- Check if `originChainSlug` or `verificationChainSlug` matches `evmxChainSlug`
73+
- If `originChainSlug == evmxChainSlug`: **Write Payload**
74+
- If `verificationChainSlug == evmxChainSlug`: **Trigger Payload**
75+
- If neither: **Message Payload**
76+
77+
## Implementation Details
78+
79+
### IdUtils.sol Functions
80+
81+
#### Encoding
82+
- `createPayloadId(originChainSlug, originId, verificationChainSlug, verificationId, pointer)`
83+
- Creates new payload ID with all components
84+
85+
#### Decoding
86+
- `decodePayloadId(payloadId)` - Full decode
87+
- `getVerificationInfo(payloadId)` - Gets verification components (for Socket routing)
88+
- `getOriginInfo(payloadId)` - Gets origin components (for source verification)
89+
90+
### Required Updates
91+
92+
1. **Watcher.sol**
93+
- Update `getCurrentPayloadId()` to use new format
94+
- Use `evmxSlug` as origin chain slug
95+
- Use hardcoded `watcherId = 1` for now
96+
- Get `dstSwitchboardId` from `switchboards` mapping
97+
98+
2. **FastSwitchboard.sol**
99+
- Add state variables: `evmxChainSlug`, `watcherId` (with onlyOwner setters)
100+
- Implement `processPayload()` to create payload ID
101+
- Add counter: `uint64 public triggerPayloadCounter`
102+
- Use: `origin = (chainSlug, switchboardId)`, `verification = (evmxChainSlug, watcherId)`
103+
104+
3. **MessageSwitchboard.sol**
105+
- Update `_createDigestAndPayloadId()` to use new format
106+
- Use: `origin = (chainSlug, switchboardId)`, `verification = (dstChainSlug, dstSwitchboardId)`
107+
108+
4. **Socket.sol**
109+
- Update `execute()` to decode payload ID and verify verification components
110+
- Remove old `createPayloadId` usage
111+
- Use `getVerificationInfo()` to extract routing info
112+
113+
5. **SocketConfig.sol**
114+
- Update `plugSwitchboardIds` type from `uint64` to `uint32` if needed (or keep uint64 and cast)
115+
116+
## Security Considerations
117+
118+
### Verification Flow
119+
1. **Destination (Socket)**: Verifies verification component matches local config
120+
2. **Source (Watcher offchain)**: Verifies origin component matches expected source
121+
3. **Pointer verification**: Skipped for now (to be added later)
122+
123+
### Counter Management
124+
- Each switchboard maintains its own counter
125+
- Prevents cross-switchboard collisions
126+
- Counters are monotonic (never decrease)
127+
128+
### ID Uniqueness
129+
- Guaranteed by switchboard-specific counters
130+
- Origin + Verification provide additional context
131+
- Reserved bits allow future expansion without breaking changes
132+
133+
## Migration Notes
134+
135+
- No production deployments yet, so no migration needed
136+
- All existing test code will need updates
137+
- Backward compatibility not required
138+
139+
## Future Enhancements
140+
141+
- Add pointer verification mechanism
142+
- Use reserved bits for additional metadata (payload version, flags, etc.)
143+
- Support multiple watchers (remove hardcoded watcherId = 1)
144+

contracts/evmx/watcher/Watcher.sol

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,15 @@ contract Watcher is Initializable, Configurations {
263263
bytes32 switchboardType_
264264
) public view returns (bytes32) {
265265
uint64 switchboardId = switchboards[chainSlug_][switchboardType_];
266-
return createPayloadId(nextPayloadCount, switchboardId, chainSlug_);
266+
// Write payload: origin = (evmxChainSlug, watcherId), verification = (dstChainSlug, dstSwitchboardId)
267+
// watcherId hardcoded as 1 for now
268+
return createPayloadId(
269+
evmxSlug, // origin chain slug (evmx)
270+
1, // origin id (watcher id, hardcoded)
271+
chainSlug_, // verification chain slug (destination)
272+
uint32(switchboardId), // verification id (destination switchboard)
273+
uint64(nextPayloadCount) // pointer (counter)
274+
);
267275
}
268276

269277
/// @notice Read a simple payload by id.

contracts/protocol/Socket.sol

Lines changed: 37 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ pragma solidity ^0.8.21;
44
import "./SocketUtils.sol";
55

66
import {WRITE} from "../utils/common/Constants.sol";
7-
import {createPayloadId} from "../utils/common/IdUtils.sol";
7+
import {getVerificationInfo} from "../utils/common/IdUtils.sol";
88

99
/**
1010
* @title Socket
@@ -31,7 +31,10 @@ contract Socket is SocketUtils {
3131
error LowGasLimit();
3232
/// @notice Thrown when the message value is insufficient
3333
error InsufficientMsgValue();
34-
34+
/// @notice Thrown when the verification chain slug is invalid
35+
error InvalidVerificationChainSlug();
36+
/// @notice Thrown when the verification switchboard id is invalid
37+
error InvalidVerificationSwitchboardId();
3538
/**
3639
* @notice Constructor for the Socket contract
3740
* @param chainSlug_ The chain slug
@@ -70,11 +73,15 @@ contract Socket is SocketUtils {
7073
if (msg.value < executeParams_.value + transmissionParams_.socketFees)
7174
revert InsufficientMsgValue();
7275

73-
bytes32 payloadId = createPayloadId(
74-
executeParams_.payloadPointer,
75-
switchboardId,
76-
chainSlug
77-
);
76+
// Get payloadId from executeParams
77+
bytes32 payloadId = executeParams_.payloadId;
78+
79+
// Verify payload ID matches destination
80+
(uint32 verificationChainSlug, uint32 verificationSwitchboardId) = getVerificationInfo(payloadId);
81+
if (verificationChainSlug != chainSlug)
82+
revert InvalidVerificationChainSlug();
83+
if (verificationSwitchboardId != uint32(switchboardId))
84+
revert InvalidVerificationSwitchboardId();
7885

7986
// validate the execution status
8087
_validateExecutionStatus(payloadId);
@@ -93,7 +100,7 @@ contract Socket is SocketUtils {
93100
* @notice Verifies the digest of the payload
94101
* @param payloadId_ The id of the payload
95102
* @param switchboardId_ The id of the switchboard
96-
* @param executeParams_ The execution parameters (appGatewayId, value, payloadPointer, callType, gasLimit)
103+
* @param executeParams_ The execution parameters (appGatewayId, value, payloadId, callType, gasLimit)
97104
* @param transmitterProof_ The transmitter proof
98105
*/
99106
function _verify(
@@ -129,7 +136,7 @@ contract Socket is SocketUtils {
129136
/**
130137
* @notice Executes the payload
131138
* @param payloadId_ The id of the payload
132-
* @param executeParams_ The execution parameters (appGatewayId, value, payloadPointer, callType, gasLimit)
139+
* @param executeParams_ The execution parameters (appGatewayId, value, payloadId, callType, gasLimit)
133140
* @param transmissionParams_ The transmission parameters (socketFees, transmitterProof, refundAddress)
134141
*/
135142
function _execute(
@@ -185,52 +192,43 @@ contract Socket is SocketUtils {
185192
}
186193

187194
////////////////////////////////////////////////////////
188-
////////////////////// Trigger //////////////////////
195+
////////////////////// Outbound Payloads //////////////////////
189196
////////////////////////////////////////////////////////
190197

191198
/**
192-
* @notice To trigger to a connected remote chain. Should only be called by a plug.
193-
* @param data_ The data to trigger the app gateway
194-
* @return triggerId The id of the trigger
199+
* @notice Sends a payload to a connected remote chain (used for both triggers and messages)
200+
* @dev Should only be called by a plug. The switchboard will create the payload ID.
201+
* @param data_ The payload data
202+
* @return payloadId The created payload ID
195203
*/
196-
function triggerAppGateway(bytes calldata data_) external payable returns (bytes32 triggerId) {
197-
triggerId = _triggerAppGateway(msg.sender, msg.value, data_);
204+
function sendPayload(bytes calldata data_) external payable returns (bytes32 payloadId) {
205+
payloadId = _sendPayload(msg.sender, msg.value, data_);
198206
}
199207

200208
/**
201-
* @notice To trigger to a connected remote chain. Should only be called by a plug.
209+
* @notice Internal function to send a payload to a connected remote chain
202210
* @param plug_ The address of the plug
203-
* @param value_ The value to trigger the app gateway
204-
* @param data_ The data to trigger the app gateway
205-
* @return triggerId The id of the trigger
211+
* @param value_ The value to send with the payload
212+
* @param data_ The payload data
213+
* @return payloadId The created payload ID from the switchboard
206214
*/
207-
function _triggerAppGateway(
215+
function _sendPayload(
208216
address plug_,
209217
uint256 value_,
210218
bytes calldata data_
211-
) internal returns (bytes32 triggerId) {
219+
) internal returns (bytes32 payloadId) {
212220
(uint64 switchboardId, address switchboardAddress) = _verifyPlugSwitchboard(plug_);
213221
bytes memory plugOverrides = IPlug(plug_).overrides();
214-
triggerId = _encodeTriggerId();
215222

216-
// todo: need gas limit?
217-
ISwitchboard(switchboardAddress).processTrigger{value: value_}(
223+
// Switchboard creates the payload ID and emits PayloadRequested event
224+
payloadId = ISwitchboard(switchboardAddress).processPayload{value: value_}(
218225
plug_,
219-
triggerId,
220226
data_,
221227
plugOverrides
222228
);
223-
224-
emit AppGatewayCallRequested(
225-
triggerId,
226-
bytes32(0), // TODO: clean this up
227-
switchboardId,
228-
toBytes32Format(plug_),
229-
plugOverrides,
230-
data_
231-
);
232229
}
233230

231+
234232
/**
235233
* @notice Increase fees for a pending payload
236234
* @param payloadId_ The payload ID to increase fees for
@@ -256,13 +254,13 @@ contract Socket is SocketUtils {
256254
switchboardAddress = switchboardAddresses[switchboardId];
257255
}
258256
/**
259-
* @notice Fallback function that forwards all calls to Socket's callAppGateway
260-
* @dev The calldata is passed as-is to the gateways
261-
* @return The trigger id
257+
* @notice Fallback function that forwards all calls to Socket's sendPayload
258+
* @dev The calldata is passed as-is to the switchboard
259+
* @return The payload ID
262260
*/
263261
fallback(bytes calldata) external payable returns (bytes memory) {
264-
// return the trigger id
265-
return abi.encode(_triggerAppGateway(msg.sender, msg.value, msg.data));
262+
// return the payload ID
263+
return abi.encode(_sendPayload(msg.sender, msg.value, msg.data));
266264
}
267265

268266
/**

contracts/protocol/interfaces/ISocket.sol

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,22 @@ interface ISocket {
5555
bytes payload
5656
);
5757

58+
/**
59+
* @notice Event emitted when a payload is requested (for both triggers and messages)
60+
* @param payloadId The created payload ID
61+
* @param plug The source plug address
62+
* @param switchboardId The switchboard ID processing the request
63+
* @param overrides The override parameters
64+
* @param payload The payload data
65+
*/
66+
event PayloadRequested(
67+
bytes32 indexed payloadId,
68+
address indexed plug,
69+
uint64 indexed switchboardId,
70+
bytes overrides,
71+
bytes payload
72+
);
73+
5874
/**
5975
* @notice Executes a payload
6076
* @param executeParams_ The execution parameters
@@ -136,7 +152,7 @@ interface ISocket {
136152
*/
137153
function switchboardAddresses(uint64 switchboardId_) external view returns (address);
138154

139-
function triggerAppGateway(bytes calldata data_) external payable returns (bytes32 triggerId);
155+
function sendPayload(bytes calldata data_) external payable returns (bytes32 payloadId);
140156

141157
function increaseFeesForPayload(bytes32 payloadId_, bytes calldata feesData_) external payable;
142158
}

contracts/protocol/interfaces/ISwitchboard.sol

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,19 @@ interface ISwitchboard {
1818
function allowPayload(bytes32 digest_, bytes32 payloadId_, address target_, bytes memory source_) external view returns (bool);
1919

2020
/**
21-
* @notice Processes a trigger and creates payload
22-
* @dev This function is called by the socket to process a trigger
21+
* @notice Processes a payload request and creates payload ID
22+
* @dev This function is called by the socket to process a payload request
2323
* @dev sb can override this function to add additional logic
24-
* @param triggerId_ Trigger ID from socket
2524
* @param plug_ Source plug address
2625
* @param payload_ Payload data
27-
* @param overrides_ Overrides for the trigger
26+
* @param overrides_ Overrides for the payload (e.g., destination chain, gas limit, fees)
27+
* @return payloadId_ The created payload ID
2828
*/
29-
function processTrigger(
29+
function processPayload(
3030
address plug_,
31-
bytes32 triggerId_,
3231
bytes calldata payload_,
3332
bytes calldata overrides_
34-
) external payable;
33+
) external payable returns (bytes32 payloadId_);
3534

3635
/**
3736
* @notice Gets the transmitter for a given payload

0 commit comments

Comments
 (0)