Skip to content

Commit

Permalink
Feat: module data passing (#2205)
Browse files Browse the repository at this point in the history
* Add separate  for module entries with aux data

* InterchainModule: handle relayed entry+data

* InterchainModule: fill module data on src chain

* Adjust tests

* Introduce SynapseGasOracle

* SynapseModule: fill gas data

* SynapseModule: receive gas data

* Update gas oracle mock

* Update GasOracleMock references

* Update packages/contracts-communication/contracts/modules/SynapseModule.sol

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update configs

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
  • Loading branch information
ChiTimesChi and coderabbitai[bot] authored Mar 8, 2024
1 parent 4d7f846 commit 0afc282
Show file tree
Hide file tree
Showing 17 changed files with 200 additions and 133 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"executorEOA": "0xD0D45E468ADF3aCB8A98391ff47267783220ba6F",
"gasOracleName": "GasOracleMock"
"gasOracleName": "SynapseGasOracleMock"
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"claimFeeBPS": 10,
"feeCollector": "0xD0D45E468ADF3aCB8A98391ff47267783220ba6F",
"gasOracleName": "GasOracleMock",
"gasOracleName": "SynapseGasOracleMock",
"threshold": 6,
"verifiers": [
"0x0E399F695d72033d598aC2911B8A5e9986d6cbaD",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,7 @@ abstract contract SynapseModuleEvents {

event ClaimFeeFractionChanged(uint256 claimFeeFraction);
event FeesClaimed(address feeCollector, uint256 collectedFees, address claimer, uint256 claimerFee);

event GasDataSent(uint256 dstChainId, bytes data);
event GasDataReceived(uint256 srcChainId, bytes data);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IGasOracle} from "./IGasOracle.sol";

interface ISynapseGasOracle is IGasOracle {
/// @notice Allows Synapse Module to pass the gas data from a remote chain to the Gas Oracle.
/// @dev Could only be called by Synapse Module.
/// @param srcChainId The chain id of the remote chain.
/// @param data The gas data from the remote chain.
function receiveRemoteGasData(uint256 srcChainId, bytes calldata data) external;

/// @notice Gets the gas data for the local chain.
function getLocalGasData() external view returns (bytes memory);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ interface ISynapseModule is IInterchainModule {
error SynapseModule__ClaimFeeFractionExceedsMax(uint256 claimFeeFraction);
error SynapseModule__FeeCollectorNotSet();
error SynapseModule__GasOracleNotContract(address gasOracle);
error SynapseModule__GasOracleNotSet();
error SynapseModule__NoFeesToClaim();

// ═══════════════════════════════════════════════ PERMISSIONED ════════════════════════════════════════════════════
Expand Down
32 changes: 32 additions & 0 deletions packages/contracts-communication/contracts/libs/ModuleEntry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {InterchainEntry} from "./InterchainEntry.sol";

library ModuleEntryLib {
/// @notice Encodes the InterchainEntry and the auxiliary module data into a single bytes array
/// @param entry The InterchainEntry to encode
/// @param moduleData The auxiliary module data to encode
function encodeModuleEntry(
InterchainEntry memory entry,
bytes memory moduleData
)
internal
pure
returns (bytes memory)
{
return abi.encode(entry, moduleData);
}

/// @notice Decodes the bytes array into the InterchainEntry and the auxiliary module data
/// @param encodedModuleEntry The bytes array to decode
/// @return entry The decoded InterchainEntry
/// @return moduleData The decoded auxiliary module data
function decodeModuleEntry(bytes memory encodedModuleEntry)
internal
pure
returns (InterchainEntry memory entry, bytes memory moduleData)
{
return abi.decode(encodedModuleEntry, (InterchainEntry, bytes));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {InterchainModuleEvents} from "../events/InterchainModuleEvents.sol";
import {IInterchainDB} from "../interfaces/IInterchainDB.sol";
import {IInterchainModule} from "../interfaces/IInterchainModule.sol";

import {InterchainEntry} from "../libs/InterchainEntry.sol";
import {InterchainEntry, ModuleEntryLib} from "../libs/ModuleEntry.sol";

import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";

Expand All @@ -32,7 +32,8 @@ abstract contract InterchainModule is InterchainModuleEvents, IInterchainModule
if (msg.value < requiredFee) {
revert InterchainModule__InsufficientFee({actual: msg.value, required: requiredFee});
}
bytes memory encodedEntry = abi.encode(entry);
bytes memory moduleData = _fillModuleData(destChainId, entry.dbNonce);
bytes memory encodedEntry = ModuleEntryLib.encodeModuleEntry(entry, moduleData);
bytes32 ethSignedEntryHash = MessageHashUtils.toEthSignedMessageHash(keccak256(encodedEntry));
_requestVerification(destChainId, encodedEntry);
emit VerificationRequested(destChainId, encodedEntry, ethSignedEntryHash);
Expand All @@ -46,20 +47,27 @@ abstract contract InterchainModule is InterchainModuleEvents, IInterchainModule
/// @dev Should be called once the Module has verified the entry and needs to signal this
/// to the InterchainDB.
function _verifyEntry(bytes memory encodedEntry) internal {
InterchainEntry memory entry = abi.decode(encodedEntry, (InterchainEntry));
(InterchainEntry memory entry, bytes memory moduleData) = ModuleEntryLib.decodeModuleEntry(encodedEntry);
if (entry.srcChainId == block.chainid) {
revert InterchainModule__SameChainId(block.chainid);
}
IInterchainDB(INTERCHAIN_DB).verifyEntry(entry);
_receiveModuleData(entry.srcChainId, entry.dbNonce, moduleData);
emit EntryVerified(
entry.srcChainId, encodedEntry, MessageHashUtils.toEthSignedMessageHash(keccak256(encodedEntry))
);
}

// solhint-disable no-empty-blocks
/// @dev Internal logic to request the verification of an entry on the destination chain.
// solhint-disable-next-line no-empty-blocks
function _requestVerification(uint256 destChainId, bytes memory encodedEntry) internal virtual {}

/// @dev Internal logic to fill the module data for the specified destination chain.
function _fillModuleData(uint256 destChainId, uint256 dbNonce) internal virtual returns (bytes memory) {}

/// @dev Internal logic to handle the auxiliary module data relayed from the remote chain.
function _receiveModuleData(uint256 srcChainId, uint256 dbNonce, bytes memory moduleData) internal virtual {}

/// @dev Internal logic to get the module fee for verifying an entry on the specified destination chain.
function _getModuleFee(uint256 destChainId) internal view virtual returns (uint256);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity 0.8.20;
import {InterchainModule} from "./InterchainModule.sol";

import {SynapseModuleEvents} from "../events/SynapseModuleEvents.sol";
import {IGasOracle} from "../interfaces/IGasOracle.sol";
import {ISynapseGasOracle} from "../interfaces/ISynapseGasOracle.sol";
import {ISynapseModule} from "../interfaces/ISynapseModule.sol";

import {ThresholdECDSA} from "../libs/ThresholdECDSA.sol";
Expand All @@ -26,6 +26,10 @@ contract SynapseModule is InterchainModule, Ownable, SynapseModuleEvents, ISynap
uint256 internal _claimFeeFraction;
/// @dev Gas limit for the verifyEntry function on the remote chain.
mapping(uint256 chainId => uint256 gasLimit) internal _verifyGasLimit;
/// @dev Hash of the last gas data sent to the remote chain.
mapping(uint256 chainId => bytes32 gasDataHash) internal _lastGasDataHash;
/// @dev Nonce of the last gas data received from the remote chain.
mapping(uint256 chainId => uint256 gasDataNonce) internal _lastGasDataNonce;

/// @inheritdoc ISynapseModule
address public feeCollector;
Expand Down Expand Up @@ -173,6 +177,45 @@ contract SynapseModule is InterchainModule, Ownable, SynapseModuleEvents, ISynap
emit VerifierRemoved(verifier);
}

/// @dev Internal logic to fill the module data for the specified destination chain.
function _fillModuleData(
uint256 destChainId,
uint256 dbNonce
)
internal
override
returns (bytes memory moduleData)
{
moduleData = _getSynapseGasOracle().getLocalGasData();
// Exit early if data is empty
if (moduleData.length == 0) {
return moduleData;
}
bytes32 dataHash = keccak256(moduleData);
// Don't send the same data twice
if (dataHash == _lastGasDataHash[destChainId]) {
moduleData = "";
} else {
_lastGasDataHash[destChainId] = dataHash;
emit GasDataSent(destChainId, moduleData);
}
}

/// @dev Internal logic to handle the auxiliary module data relayed from the remote chain.
function _receiveModuleData(uint256 srcChainId, uint256 dbNonce, bytes memory moduleData) internal override {
// Exit early if data is empty
if (moduleData.length == 0) {
return;
}
// Don't process outdated data
uint256 lastNonce = _lastGasDataNonce[srcChainId];
if (lastNonce == 0 || lastNonce < dbNonce) {
_lastGasDataNonce[srcChainId] = dbNonce;
_getSynapseGasOracle().receiveRemoteGasData(srcChainId, moduleData);
emit GasDataReceived(srcChainId, moduleData);
}
}

// ══════════════════════════════════════════════ INTERNAL VIEWS ═══════════════════════════════════════════════════

/// @dev Internal logic to get the module fee for verifying an entry on the specified destination chain.
Expand All @@ -183,10 +226,18 @@ contract SynapseModule is InterchainModule, Ownable, SynapseModuleEvents, ISynap
// entry is 32 (length) + 32*4 (fields) = 160
// signatures: 32 (length) + 65*threshold (padded up to be a multiple of 32 bytes)
// Total formula is: 4 + 32 (entry offset) + 32 (signatures offset) + 160 + 32
return IGasOracle(gasOracle).estimateTxCostInLocalUnits({
return _getSynapseGasOracle().estimateTxCostInLocalUnits({
remoteChainId: destChainId,
gasLimit: getVerifyGasLimit(destChainId),
calldataSize: 292 + 64 * getThreshold()
});
}

/// @dev Internal logic to get the Synapse Gas Oracle. Reverts if the gas oracle is not set.
function _getSynapseGasOracle() internal view returns (ISynapseGasOracle synapseGasOracle) {
synapseGasOracle = ISynapseGasOracle(gasOracle);
if (address(synapseGasOracle) == address(0)) {
revert SynapseModule__GasOracleNotSet();
}
}
}
103 changes: 0 additions & 103 deletions packages/contracts-communication/script/MessagingBase.s.sol

This file was deleted.

2 changes: 1 addition & 1 deletion packages/contracts-communication/script/testnet-deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ yarn fsr script/deploy/DeploySynapseModule.s.sol "$chainName" "$walletName" "$@"
yarn fsr-str script/deploy/DeployWithMsgSender.s.sol "$chainName" "$walletName" "ExecutionFees" "$@"
yarn fsr-str script/deploy/DeployWithMsgSender.s.sol "$chainName" "$walletName" "ExecutionService" "$@"

yarn fsr-str script/deploy/DeployNoArgs.s.sol "$chainName" "$walletName" "GasOracleMock" "$@"
yarn fsr-str script/deploy/DeployNoArgs.s.sol "$chainName" "$walletName" "SynapseGasOracleMock" "$@"
6 changes: 3 additions & 3 deletions packages/contracts-communication/test/ExecutionService.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@ pragma solidity 0.8.20;

import {ExecutionService, ExecutionServiceEvents, IExecutionService} from "../contracts/ExecutionService.sol";
import {Test} from "forge-std/Test.sol";
import {GasOracleMock} from "./mocks/GasOracleMock.sol";
import {SynapseGasOracleMock} from "./mocks/SynapseGasOracleMock.sol";

contract ExecutionServiceTest is ExecutionServiceEvents, Test {
ExecutionService public executionService;
GasOracleMock public gasOracle;
SynapseGasOracleMock public gasOracle;

address icClient = address(0x123);
address executorEOA = address(0x456);
address owner = makeAddr("Owner");

function setUp() public {
gasOracle = new GasOracleMock();
gasOracle = new SynapseGasOracleMock();
executionService = new ExecutionService(address(this));
executionService.setInterchainClient(icClient);
executionService.setExecutorEOA(executorEOA);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import {InterchainEntry, ModuleEntryLib} from "../../contracts/libs/ModuleEntry.sol";

contract ModuleEntryLibHarness {
function encodeModuleEntry(
InterchainEntry memory entry,
bytes memory moduleData
)
external
pure
returns (bytes memory)
{
return ModuleEntryLib.encodeModuleEntry(entry, moduleData);
}

function decodeModuleEntry(bytes memory encodedModuleEntry)
external
pure
returns (InterchainEntry memory, bytes memory)
{
return ModuleEntryLib.decodeModuleEntry(encodedModuleEntry);
}
}
Loading

0 comments on commit 0afc282

Please sign in to comment.