Skip to content

Commit

Permalink
Merge branch 'master' into fe/selector-component
Browse files Browse the repository at this point in the history
  • Loading branch information
abtestingalpha committed Apr 4, 2024
2 parents bf18720 + 970cc53 commit 428008b
Show file tree
Hide file tree
Showing 67 changed files with 2,959 additions and 706 deletions.
329 changes: 283 additions & 46 deletions committee/contracts/interchaindb/interchaindb.abigen.go

Large diffs are not rendered by default.

Large diffs are not rendered by default.

371 changes: 288 additions & 83 deletions committee/contracts/synapsemodule/synapsemodule.abigen.go

Large diffs are not rendered by default.

Large diffs are not rendered by default.

53 changes: 37 additions & 16 deletions packages/contracts-communication/contracts/InterchainClientV1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
} from "./libs/InterchainTransaction.sol";
import {OptionsLib, OptionsV1} from "./libs/Options.sol";
import {TypeCasts} from "./libs/TypeCasts.sol";
import {VersionedPayloadLib} from "./libs/VersionedPayload.sol";

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

Expand All @@ -26,6 +27,10 @@ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
contract InterchainClientV1 is Ownable, InterchainClientV1Events, IInterchainClientV1 {
using AppConfigLib for bytes;
using OptionsLib for bytes;
using VersionedPayloadLib for bytes;

/// @notice Version of the InterchainClient contract. Sent and received transactions must have the same version.
uint16 public constant CLIENT_VERSION = 1;

/// @notice Address of the InterchainDB contract, set at the time of deployment.
address public immutable INTERCHAIN_DB;
Expand Down Expand Up @@ -97,8 +102,9 @@ contract InterchainClientV1 is Ownable, InterchainClientV1Events, IInterchainCli
external
payable
{
InterchainTransaction memory icTx = InterchainTransactionLib.decodeTransaction(transaction);
bytes32 transactionId = _assertExecutable(icTx, proof);
InterchainTransaction memory icTx = _assertCorrectVersion(transaction);
bytes32 transactionId = keccak256(transaction);
_assertExecutable(icTx, transactionId, proof);
_txExecutor[transactionId] = msg.sender;

OptionsV1 memory decodedOptions = icTx.options.decodeOptionsV1();
Expand Down Expand Up @@ -136,17 +142,17 @@ contract InterchainClientV1 is Ownable, InterchainClientV1Events, IInterchainCli

// @inheritdoc IInterchainClientV1
function isExecutable(bytes calldata encodedTx, bytes32[] calldata proof) external view returns (bool) {
InterchainTransaction memory icTx = InterchainTransactionLib.decodeTransaction(encodedTx);
InterchainTransaction memory icTx = _assertCorrectVersion(encodedTx);
// Check that options could be decoded
icTx.options.decodeOptionsV1();
_assertExecutable(icTx, proof);
bytes32 transactionId = keccak256(encodedTx);
_assertExecutable(icTx, transactionId, proof);
return true;
}

// @inheritdoc IInterchainClientV1
function getExecutor(bytes calldata encodedTx) external view returns (address) {
InterchainTransaction memory icTx = InterchainTransactionLib.decodeTransaction(encodedTx);
return _txExecutor[icTx.transactionId()];
return _txExecutor[keccak256(encodedTx)];
}

// @inheritdoc IInterchainClientV1
Expand Down Expand Up @@ -199,16 +205,19 @@ contract InterchainClientV1 is Ownable, InterchainClientV1Events, IInterchainCli
}
}

/// @notice Encodes the transaction data into a bytes format.
function encodeTransaction(InterchainTransaction memory icTx) external pure returns (bytes memory) {
return icTx.encodeTransaction();
}

/// @notice Decodes the encoded options data into a OptionsV1 struct.
function decodeOptions(bytes memory encodedOptions) external pure returns (OptionsV1 memory) {
function decodeOptions(bytes memory encodedOptions) external view returns (OptionsV1 memory) {
return encodedOptions.decodeOptionsV1();
}

/// @notice Encodes the transaction data into a bytes format.
function encodeTransaction(InterchainTransaction memory icTx) public pure returns (bytes memory) {
return VersionedPayloadLib.encodeVersionedPayload({
version: CLIENT_VERSION,
payload: InterchainTransactionLib.encodeTransaction(icTx)
});
}

// ═════════════════════════════════════════════════ INTERNAL ══════════════════════════════════════════════════════

/// @dev Internal logic for sending a message to another chain.
Expand Down Expand Up @@ -241,7 +250,7 @@ contract InterchainClientV1 is Ownable, InterchainClientV1Events, IInterchainCli
options: options,
message: message
});
desc.transactionId = icTx.transactionId();
desc.transactionId = keccak256(encodeTransaction(icTx));
// Sanity check: nonce returned from DB should match the nonce used to construct the transaction
{
(uint256 dbNonce, uint64 entryIndex) = IInterchainDB(INTERCHAIN_DB).writeEntryWithVerification{
Expand Down Expand Up @@ -284,20 +293,19 @@ contract InterchainClientV1 is Ownable, InterchainClientV1Events, IInterchainCli

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

/// @dev Asserts that the transaction is executable. Returns the transactionId for chaining purposes.
/// @dev Asserts that the transaction is executable.
function _assertExecutable(
InterchainTransaction memory icTx,
bytes32 transactionId,
bytes32[] calldata proof
)
internal
view
returns (bytes32 transactionId)
{
bytes32 linkedClient = _assertLinkedClient(icTx.srcChainId);
if (icTx.dstChainId != block.chainid) {
revert InterchainClientV1__IncorrectDstChainId(icTx.dstChainId);
}
transactionId = icTx.transactionId();
if (_txExecutor[transactionId] != address(0)) {
revert InterchainClientV1__TxAlreadyExecuted(transactionId);
}
Expand Down Expand Up @@ -357,4 +365,17 @@ contract InterchainClientV1 is Ownable, InterchainClientV1Events, IInterchainCli
}
}
}

/// @dev Asserts that the transaction version is correct. Returns the decoded transaction for chaining purposes.
function _assertCorrectVersion(bytes calldata versionedTx)
internal
pure
returns (InterchainTransaction memory icTx)
{
uint16 version = versionedTx.getVersion();
if (version != CLIENT_VERSION) {
revert InterchainClientV1__InvalidTransactionVersion(version);
}
icTx = InterchainTransactionLib.decodeTransaction(versionedTx.getPayload());
}
}
22 changes: 19 additions & 3 deletions packages/contracts-communication/contracts/InterchainDB.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@ import {IInterchainModule} from "./interfaces/IInterchainModule.sol";

import {InterchainBatch, InterchainBatchLib} from "./libs/InterchainBatch.sol";
import {InterchainEntry, InterchainEntryLib} from "./libs/InterchainEntry.sol";
import {TypeCasts} from "./libs/TypeCasts.sol";
import {VersionedPayloadLib} from "./libs/VersionedPayload.sol";

contract InterchainDB is InterchainDBEvents, IInterchainDB {
using VersionedPayloadLib for bytes;

uint16 public constant DB_VERSION = 1;

bytes32[] internal _entryValues;
mapping(address module => mapping(bytes32 batchKey => RemoteBatch batch)) internal _remoteBatches;

Expand Down Expand Up @@ -63,7 +67,15 @@ contract InterchainDB is InterchainDBEvents, IInterchainDB {
// ═══════════════════════════════════════════════ MODULE-FACING ═══════════════════════════════════════════════════

/// @inheritdoc IInterchainDB
function verifyRemoteBatch(InterchainBatch memory batch) external onlyRemoteChainId(batch.srcChainId) {
function verifyRemoteBatch(bytes calldata versionedBatch) external {
uint16 dbVersion = versionedBatch.getVersion();
if (dbVersion != DB_VERSION) {
revert InterchainDB__InvalidBatchVersion(dbVersion);
}
InterchainBatch memory batch = InterchainBatchLib.decodeBatch(versionedBatch.getPayload());
if (batch.srcChainId == block.chainid) {
revert InterchainDB__SameChainId(batch.srcChainId);
}
bytes32 batchKey = InterchainBatchLib.batchKey(batch);
RemoteBatch memory existingBatch = _remoteBatches[msg.sender][batchKey];
// Check if that's the first time module verifies the batch
Expand Down Expand Up @@ -209,8 +221,12 @@ contract InterchainDB is InterchainDBEvents, IInterchainDB {
fees[0] += msg.value - totalFee;
}
uint256 len = srcModules.length;
bytes memory versionedBatch = VersionedPayloadLib.encodeVersionedPayload({
version: DB_VERSION,
payload: InterchainBatchLib.encodeBatch(batch)
});
for (uint256 i = 0; i < len; ++i) {
IInterchainModule(srcModules[i]).requestBatchVerification{value: fees[i]}(dstChainId, batch);
IInterchainModule(srcModules[i]).requestBatchVerification{value: fees[i]}(dstChainId, versionedBatch);
}
emit InterchainBatchVerificationRequested(dstChainId, batch.dbNonce, batch.batchRoot, srcModules);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {IExecutionService, ISynapseExecutionServiceV1} from "../interfaces/ISyna
import {SynapseExecutionServiceEvents} from "../events/SynapseExecutionServiceEvents.sol";
import {IGasOracle} from "../interfaces/IGasOracle.sol";
import {OptionsLib, OptionsV1} from "../libs/Options.sol";
import {VersionedPayloadLib} from "../libs/VersionedPayload.sol";

import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";

Expand Down Expand Up @@ -70,7 +71,7 @@ contract SynapseExecutionServiceV1 is
uint256 txPayloadSize,
bytes32 transactionId,
uint256 executionFee,
bytes memory options
bytes calldata options
)
external
virtual
Expand All @@ -87,7 +88,7 @@ contract SynapseExecutionServiceV1 is
function getExecutionFee(
uint256 dstChainId,
uint256 txPayloadSize,
bytes memory options
bytes calldata options
)
public
view
Expand All @@ -98,8 +99,9 @@ contract SynapseExecutionServiceV1 is
if (cachedGasOracle == address(0)) {
revert SynapseExecutionService__GasOracleNotSet();
}
// TODO: the "exact version" check should be generalized
(uint8 version,) = OptionsLib.decodeVersionedOptions(options);
// ExecutionServiceV1 implementation only supports Options V1.
// Following versions will be supported by the future implementations.
uint16 version = VersionedPayloadLib.getVersion(options);
if (version > OptionsLib.OPTIONS_V1) {
revert SynapseExecutionService__OptionsVersionNotSupported(version);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ interface IInterchainClientV1 {
error InterchainClientV1__FeeAmountTooLow(uint256 actual, uint256 required);
error InterchainClientV1__IncorrectDstChainId(uint256 chainId);
error InterchainClientV1__IncorrectMsgValue(uint256 actual, uint256 required);
error InterchainClientV1__InvalidTransactionVersion(uint16 version);
error InterchainClientV1__NoLinkedClient(uint256 chainId);
error InterchainClientV1__NotEnoughResponses(uint256 actual, uint256 required);
error InterchainClientV1__NotEVMClient(bytes32 client);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ interface IInterchainDB {
error InterchainDB__ConflictingBatches(address module, bytes32 existingBatchRoot, InterchainBatch newBatch);
error InterchainDB__EntryIndexOutOfRange(uint256 dbNonce, uint64 entryIndex, uint64 batchSize);
error InterchainDB__IncorrectFeeAmount(uint256 actualFee, uint256 expectedFee);
error InterchainDB__InvalidBatchVersion(uint16 version);
error InterchainDB__InvalidEntryRange(uint256 dbNonce, uint64 start, uint64 end);
error InterchainDB__NoModulesSpecified();
error InterchainDB__SameChainId(uint256 chainId);
Expand Down Expand Up @@ -68,8 +69,9 @@ interface IInterchainDB {
returns (uint256 dbNonce, uint64 entryIndex);

/// @notice Allows the Interchain Module to verify the batch coming from the remote chain.
/// @param batch The Interchain Batch to confirm
function verifyRemoteBatch(InterchainBatch memory batch) external;
/// Note: The DB will only accept the batch of the same version as the DB itself.
/// @param versionedBatch The versioned Interchain Batch to verify
function verifyRemoteBatch(bytes memory versionedBatch) external;

// ═══════════════════════════════════════════════════ VIEWS ═══════════════════════════════════════════════════════

Expand Down Expand Up @@ -157,4 +159,8 @@ interface IInterchainDB {
external
view
returns (uint256 moduleVerifiedAt);

/// @notice Get the version of the Interchain DataBase.
// solhint-disable-next-line func-name-mixedcase
function DB_VERSION() external pure returns (uint16);
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IInterchainDB} from "./IInterchainDB.sol";
import {InterchainBatch} from "../libs/InterchainBatch.sol";

/// @notice Every Module may opt a different method to confirm the verified entries on destination chain,
/// therefore this is not a part of a common interface.
interface IInterchainModule {
Expand All @@ -19,9 +16,9 @@ interface IInterchainModule {
/// Note: this will eventually trigger `InterchainDB.verifyRemoteBatch(batch)` function on destination chain,
/// with no guarantee of ordering.
/// @dev Could be only called by the Interchain DataBase contract.
/// @param dstChainId The chain id of the destination chain
/// @param batch The batch to verify
function requestBatchVerification(uint256 dstChainId, InterchainBatch memory batch) external payable;
/// @param dstChainId The chain id of the destination chain
/// @param versionedBatch The versioned batch to verify
function requestBatchVerification(uint256 dstChainId, bytes memory versionedBatch) external payable;

/// @notice Get the Module fee for verifying a batch on the specified destination chain.
/// @param dstChainId The chain id of the destination chain
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {IExecutionService} from "./IExecutionService.sol";
interface ISynapseExecutionServiceV1 is IExecutionService {
error SynapseExecutionService__GasOracleNotSet();
error SynapseExecutionService__FeeAmountTooLow(uint256 actual, uint256 required);
error SynapseExecutionService__OptionsVersionNotSupported(uint256 version);
error SynapseExecutionService__OptionsVersionNotSupported(uint16 version);
error SynapseExecutionService__ZeroAddress();

/// @notice Allows the contract governor to set the address of the EOA account that will be used
Expand Down
43 changes: 14 additions & 29 deletions packages/contracts-communication/contracts/libs/AppConfig.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import {VersionedPayloadLib} from "./VersionedPayload.sol";

struct AppConfigV1 {
uint256 requiredResponses;
uint256 optimisticPeriod;
Expand All @@ -9,45 +11,28 @@ struct AppConfigV1 {
using AppConfigLib for AppConfigV1 global;

library AppConfigLib {
uint8 constant APP_CONFIG_V1 = 1;

error AppConfigLib__IncorrectVersion(uint8 version);
using VersionedPayloadLib for bytes;

/// @notice Encodes versioned app config into a bytes format.
/// @param version The version of the app config.
/// @param appConfig The app config to encode.
function encodeVersionedAppConfig(uint8 version, bytes memory appConfig) internal pure returns (bytes memory) {
return abi.encode(version, appConfig);
}
uint16 internal constant APP_CONFIG_V1 = 1;

/// @notice Decodes versioned app config from a bytes format back into a version and app config.
/// @param data The versioned app config data in bytes format.
/// @return version The version of the app config.
/// @return appConfig The app config as bytes.
function decodeVersionedAppConfig(bytes memory data)
internal
pure
returns (uint8 version, bytes memory appConfig)
{
(version, appConfig) = abi.decode(data, (uint8, bytes));
}

/// @notice Encodes V1 app config into a bytes format.
/// @param appConfig The AppConfigV1 to encode.
function encodeAppConfigV1(AppConfigV1 memory appConfig) internal pure returns (bytes memory) {
return encodeVersionedAppConfig(APP_CONFIG_V1, abi.encode(appConfig));
}
error AppConfigLib__IncorrectVersion(uint16 version);

/// @notice Decodes app config (V1 or higher) from a bytes format back into an AppConfigV1 struct.
/// @param data The app config data in bytes format.
function decodeAppConfigV1(bytes memory data) internal pure returns (AppConfigV1 memory) {
(uint8 version, bytes memory appConfig) = decodeVersionedAppConfig(data);
function decodeAppConfigV1(bytes memory data) internal view returns (AppConfigV1 memory) {
uint16 version = data.getVersionFromMemory();
if (version < APP_CONFIG_V1) {
revert AppConfigLib__IncorrectVersion(version);
}
// Structs of the same version will always be decoded correctly.
// Following versions will be decoded correctly if they have the same fields as the previous version,
// and new fields at the end: abi.decode ignores the extra bytes in the decoded payload.
return abi.decode(appConfig, (AppConfigV1));
return abi.decode(data.getPayloadFromMemory(), (AppConfigV1));
}

/// @notice Encodes V1 app config into a bytes format.
/// @param appConfig The AppConfigV1 to encode.
function encodeAppConfigV1(AppConfigV1 memory appConfig) internal pure returns (bytes memory) {
return VersionedPayloadLib.encodeVersionedPayload(APP_CONFIG_V1, abi.encode(appConfig));
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {VersionedPayloadLib} from "./VersionedPayload.sol";

/// @notice Struct representing a batch of entries in the Interchain DataBase.
/// Batched entries are put together in a Merkle tree, which root is saved.
/// Batch has a globally unique identifier (key) and a value.
Expand All @@ -17,6 +19,8 @@ struct InterchainBatch {
}

library InterchainBatchLib {
using VersionedPayloadLib for bytes;

/// @notice Constructs an InterchainBatch struct to be saved on the local chain.
/// @param dbNonce The database nonce of the batch
/// @param batchRoot The root of the Merkle tree containing the batched entries
Expand All @@ -32,6 +36,21 @@ library InterchainBatchLib {
return InterchainBatch({srcChainId: block.chainid, dbNonce: dbNonce, batchRoot: batchRoot});
}

/// @notice Encodes the InterchainBatch struct into a non-versioned batch payload.
function encodeBatch(InterchainBatch memory batch) internal pure returns (bytes memory) {
return abi.encode(batch);
}

/// @notice Decodes the InterchainBatch struct from a non-versioned batch payload in calldata.
function decodeBatch(bytes calldata data) internal pure returns (InterchainBatch memory) {
return abi.decode(data, (InterchainBatch));
}

/// @notice Decodes the InterchainBatch struct from a non-versioned batch payload in memory.
function decodeBatchFromMemory(bytes memory data) internal pure returns (InterchainBatch memory) {
return abi.decode(data, (InterchainBatch));
}

/// @notice Returns the globally unique identifier of the batch
function batchKey(InterchainBatch memory batch) internal pure returns (bytes32) {
return keccak256(abi.encode(batch.srcChainId, batch.dbNonce));
Expand Down
Loading

0 comments on commit 428008b

Please sign in to comment.