Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions FunctionSignatures.md
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@
| `isAttested` | `0xc13c2396` |
| `owner` | `0x8da5cb5b` |
| `ownershipHandoverExpiresAt` | `0xfee81cf4` |
| `processTrigger` | `0x7f3352bc` |
| `processPayload` | `0x7f3352bc` |
| `registerSwitchboard` | `0x74f5b1fc` |
| `renounceOwnership` | `0x715018a6` |
| `requestOwnershipHandover` | `0x25692962` |
Expand All @@ -389,7 +389,7 @@
| `owner` | `0x8da5cb5b` |
| `ownershipHandoverExpiresAt` | `0xfee81cf4` |
| `payloadCounter` | `0x550ce1d5` |
| `processTrigger` | `0x7f3352bc` |
| `processPayload` | `0x7f3352bc` |
| `registerSibling` | `0x4f58b88c` |
| `registerSwitchboard` | `0x74f5b1fc` |
| `renounceOwnership` | `0x715018a6` |
Expand Down
144 changes: 144 additions & 0 deletions PAYLOAD_ID_ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -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)

6 changes: 3 additions & 3 deletions contracts/evmx/interfaces/IConfigurations.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion contracts/evmx/interfaces/IWatcher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
3 changes: 1 addition & 2 deletions contracts/evmx/plugs/FeesPlug.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ contract FeesPlug is IFeesPlug, PlugBase, AccessControl {
constructor(address socket_, address owner_) {
_setSocket(socket_);
_initializeOwner(owner_);

isSocketInitialized = 1;
}

Expand Down Expand Up @@ -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_);
}
Expand Down
10 changes: 5 additions & 5 deletions contracts/evmx/watcher/Configurations.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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_);
Expand All @@ -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
Expand All @@ -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();
Expand Down
14 changes: 11 additions & 3 deletions contracts/evmx/watcher/Watcher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down
Loading