# Telepathy Validator Documentation ## Introduction The [Telepathy Protocol](https://www.telepathy.xyz/) has been [launched](https://twitter.com/SuccinctLabs/status/1636429568172228609), which removes the need to trust centralized actors or permissioned multisigs when passing information between chains. For developers wishing to use Telepathy for cross-chain messaging, the standard path is to plug into the [Telepathy Router](https://docs.telepathy.xyz/protocol/contracts#router) by using the send function: ```Solidity send(uint32 destinationChainId, bytes32 destinationAddress, bytes calldata data) ``` For *existing* contracts that are cannot be modified to make this external call, Telepathy provides a different system: **On-Chain PubSub for events**. Since the [TokenBridge contracts](https://github.com/omni/tokenbridge-contracts) fall into the category, we will demonstrate how this system is used to create a Validator secured by the [Telepathy Light Client](https://docs.telepathy.xyz/protocol/contracts#light-client): ![gnosis-validator](https://i.imgur.com/Be6IAg7.png) For additional information on the protocol, please refer to the official [Telepathy docs](https://docs.telepathy.xyz/). This specific document will focus on the PubSub system and the Validator. Telepathy PubSub Technical Walkthrough --- To listen to any arbitrary event that was emitted on-chain, a contract must first [subscribe](https://github.com/succinctlabs/telepathy-contracts/blob/9ed099dc2eed7a000b2b3d5309da8ccf813a896d/src/pubsub/interfaces/IPubSub.sol#L45-L52) to it: ```Solidity function subscribe( uint32 sourceChainId, address sourceAddress, address callbackAddress, bytes32 eventSig, uint64 startSlot, uint64 endSlot ) external returns (bytes32 subscriptionId) ``` Then, the subscribed contract specified by `callbackAddress` will receive updates from the [publisher](https://github.com/succinctlabs/telepathy-contracts/blob/9ed099dc2eed7a000b2b3d5309da8ccf813a896d/src/pubsub/TelepathyPublisher.sol#L16) whenever a new event is emitted for that `sourceChainId` + `sourceAddress` + `eventSig`. All events that are published are verified with the Light Client and event proof in [publishEvent()](https://github.com/succinctlabs/telepathy-contracts/blob/9ed099dc2eed7a000b2b3d5309da8ccf813a896d/src/pubsub/TelepathyPublisher.sol#L26). The subscribed contract *must* implement [ISubscriptionReceiver](https://github.com/succinctlabs/telepathy-contracts/blob/9ed099dc2eed7a000b2b3d5309da8ccf813a896d/src/pubsub/interfaces/ISubscriptionReceiver.sol#L3). We also provide an abstract contract, [SubscriptionReceiver](https://github.com/succinctlabs/telepathy-contracts/blob/9ed099dc2eed7a000b2b3d5309da8ccf813a896d/src/pubsub/interfaces/SubscriptionReceiver.sol#L6), for ensuring that all the checks for handling this event are met. This means that all that needs to be done is implementing this function: ```Solidity function handlePublishImpl( bytes32 _subscriptionId, uint32 _sourceChainId, address _sourceAddress, uint64 _slot, bytes32[] memory _eventTopics, bytes memory _eventdata ) internal virtual; ``` Gnosis Validator Technical Walkthrough --- To create a trustless ETH->Gnosis Validator on top of this system, we simply subscribe to the [UserRequestForAffirmation](https://github.com/succinctlabs/tokenbridge-contracts/blob/908a48107919d4ab127f9af07d44d47eac91547e/contracts/upgradeable_contracts/arbitrary_message/ForeignAMB.sol#L6) event emitted by the [ForeignAMB](https://github.com/succinctlabs/tokenbridge-contracts/blob/908a48107919d4ab127f9af07d44d47eac91547e/contracts/upgradeable_contracts/arbitrary_message/ForeignAMB.sol#L5): ```Solidity event UserRequestForAffirmation(bytes32 indexed messageId, bytes encodedData); ``` In the [Telepathy Validator](https://github.com/succinctlabs/telepathy-contracts/blob/9ed099dc2eed7a000b2b3d5309da8ccf813a896d/examples/pubsub/gnosis/TelepathyValidator.sol#L11) contract deployed on Gnosis, we specify the Ethereum ForeignAMB and the event via its signature: ```Solidity bytes32 constant AFFIRMATION_EVENT_SIG = keccak256("UserRequestForAffirmation(bytes32,bytes)"); function subscribeToAMBAffirmationEvent() external onlyOwner returns (bytes32) { ambAffirmationSubscriptionId = telepathyPubSub.subscribe( SOURCE_CHAIN_ID, AMB_AFFIRMATION_SOURCE_ADDRESS, address(this), AMB_AFFIRMATION_EVENT_SIG, START_SLOT, END_SLOT ); return ambAffirmationSubscriptionId; } ``` The contract now receives updates whenever this event is emitted. It handles these in the [handlePublishImpl](https://github.com/succinctlabs/telepathy-contracts/blob/9ed099dc2eed7a000b2b3d5309da8ccf813a896d/examples/pubsub/gnosis/TelepathyValidator.sol#L67) function, where additional validation is done: ```Solidity /// @notice Handle the published AMBAffirmation or BridgeAffirmation event by executing the /// affirmation in the HomeAMB or HomeBridge. function handlePublishImpl( bytes32 _subscriptionId, uint32 _sourceChainId, address _sourceAddress, uint64 _slot, bytes32 _publishKey, bytes32[] memory _eventTopics, bytes memory _eventData ) internal override { if (_sourceChainId != SOURCE_CHAIN_ID) { revert InvalidSourceChain(_sourceChainId); } if ( _sourceAddress != AMB_AFFIRMATION_SOURCE_ADDRESS && _sourceAddress != BRIDGE_AFFIRMATION_SOURCE_ADDRESS ) { revert InvalidSourceAddress(_sourceAddress); } if ( _subscriptionId != ambAffirmationSubscriptionId && _subscriptionId != bridgeAffirmationSubscriptionId ) { revert InvalidSubscriptionId(_subscriptionId); } if (_slot < START_SLOT || (END_SLOT != 0 && _slot > END_SLOT)) { revert InvalidSlot(_slot); } bytes32 eventSig = _eventTopics[0]; if (eventSig == AMB_AFFIRMATION_EVENT_SIG) { bytes32 messageId = _eventTopics[1]; if (executeAffirmationsEnabled) { // abi.decode strips away the added offset+length prefix, which is added // by Solidity for all dynamic types. bytes memory eventData = abi.decode(_eventData, (bytes)); HOME_AMB.executeAffirmation(eventData); } emit AMBAffirmationHandled(_publishKey, messageId, _eventData); } else if (eventSig == BRIDGE_AFFIRMATION_EVENT_SIG) { (address recipient, uint256 value) = abi.decode(_eventData, (address, uint256)); bytes32 affirmationKey = keccak256(abi.encode(recipient, value)); if (bridgeAffirmationStatuses[affirmationKey] != ExecutionStatus.NONE) { revert AffirmationNotNew(recipient, value); } bridgeAffirmationStatuses[affirmationKey] = ExecutionStatus.PENDING; emit BridgeAffirmationHandled(_publishKey, recipient, value); } } ``` When this contract is added as a Validator, `executeAffirmationsEnabled` can be set to true and the HomeAMB's [executeAffirmation()](https://github.com/succinctlabs/tokenbridge-contracts/blob/908a48107919d4ab127f9af07d44d47eac91547e/contracts/upgradeable_contracts/arbitrary_message/BasicHomeAMB.sol#L20) function. For now, we emit `AffirmationHandled` with the event data. Results (AffirmationHandled Subscription) --- The Telepathy Validator contract is deployed, and the source code can be viewed on GnosisScan [here](https://gnosisscan.io/address/0xbfe15ccbf28c504ddcebcef60753abfff731a675#code). As shown in the [events](https://gnosisscan.io/address/0xbfe15ccbf28c504ddcebcef60753abfff731a675#events) tab, we can see these results successfully handled: ![https://gnosisscan.io/address/0xbfe15ccbf28c504ddcebcef60753abfff731a675#events](https://i.imgur.com/TrTsoPw.png) You can also find this and the PubSub system in our [contracts repository](https://github.com/succinctlabs/telepathy-contracts) on GitHub. Results (Succesfull Token Transfer) --- Token Transfer Tx: https://gnosisscan.io/tx/0x8bc4bdf3db73b5a563e7181451daaf3ec4b4b4579e85a17c9b33767842c1be5f ![](https://i.imgur.com/fQ7KFoB.jpg) Contracts --- The working testnet validator is deployed at: [0x8982285539cdd25981e27a84E049aa6390Ab01eD](https://gnosisscan.io/address/0x8982285539cdd25981e27a84e049aa6390ab01ed.). All the smart contracts related to the Telepathy Validator are available in our [open-source repo](https://github.com/succinctlabs/telepathy-contracts). The main components consist of the: - [Light Client](https://github.com/succinctlabs/telepathy-contracts/blob/main/src/lightclient/LightClient.sol): A contract which uses our [sync committee zkSNARK](https://github.com/succinctlabs/telepathy-circuits) to keep Gnosis Chain updated with Ethereum headers. - [Protocol Explanation](https://docs.telepathy.xyz/protocol/overview) - [Telepathy PubSub](https://github.com/succinctlabs/telepathy-contracts/blob/main/src/pubsub/TelepathyPubSub.sol): A contract which allows one to "subscribe" to events emitted on Ethereum on other chains. - Refer to the diagram above for an explanation. - [Telepathy Validator](https://github.com/succinctlabs/telepathy-contracts/blob/main/external/integrations/gnosis/TelepathyValidator.sol): A contract which uses the "PubSub" contract to be notified about affirmation requests and calls back to the Omnibrdige with `executeAffirmation`. - Refer to the diagram above for an explanation. For more details on how both of these systems works, refer to the code and comments. Relayer --- We have not yet open-sourced the relayer as it is interconnected with our monorepo. Given that we are running the relayer, please let us know if it is necessary to open-source it in the short-term. We plan to add the following: - Replicas - Metrics & logging with [Datadog](https://www.datadoghq.com/) - 24/7 on-call with [Pagerduty](https://www.pagerduty.com/) Please let us know if there is anything else Gnosis DevOps advises us to add. Audits --- - [Light Client](https://github.com/succinctlabs/telepathy-contracts/blob/main/src/lightclient/LightClient.sol): **This contract does need an audit.** It was audited by Trail of Bits, Veridise, and Zellic in early 2023. - [Telepathy PubSub](https://github.com/succinctlabs/telepathy-contracts/blob/main/src/pubsub/TelepathyPubSub.sol): **This contract needs an audit.** However, the grand majority of the complexity are betwen lines [L35-L65](https://github.com/succinctlabs/telepathy-contracts/blob/main/src/pubsub/TelepathyPublisher.sol#L35-L64) which dispatches to our libraries which are all audited. - In particular, the following libraries are audited. - `src/libraries/SimpleSerialize.sol` - `src/libraries/Typecast.sol` - Extra Notes: - `src/pubsub/EventProof.sol`: This library is not directly audited, but it is heavily based on [StateProofHelper.sol](https://github.com/succinctlabs/telepathy-contracts/blob/main/src/libraries/StateProofHelper.sol#L36-L101) which was audited by Trail of Bits and Zellic. We needed to make changes to this file as we also needed the contents of the event, not just the hash. - [Telepathy Validator](https://github.com/succinctlabs/telepathy-contracts/blob/main/external/integrations/gnosis/TelepathyValidator.sol): **This contract needs an audit.** In aggregate, we suggest auditing the following folders: - [src/pubsub](https://github.com/succinctlabs/telepathy-contracts/tree/main/src/pubsub) - [external/integrations/gnosis](https://github.com/succinctlabs/telepathy-contracts/tree/main/external/integrations/gnosis) Unit tests are available for all contracts using foundry: - [test/pubsub](https://github.com/succinctlabs/telepathy-contracts/tree/main/test/pubsub) - [external/integrations/gnosis/TelepathyValidator.t.sol](https://github.com/succinctlabs/telepathy-contracts/blob/main/external/integrations/gnosis/TelepathyValidator.t.sol) Testing Contracts --- To build and test the contracts, follow the following commands ``` curl -L https://foundry.paradigm.xyz | bash source ~/.bashrc foundryup forge build forge test ``` All tests should pass.