Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Adding logic to check more L2 chains fo guard CM #122

Merged
merged 15 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
307 changes: 73 additions & 234 deletions contracts/multisigs/GuardCM.sol

Large diffs are not rendered by default.

37 changes: 37 additions & 0 deletions contracts/multisigs/VerifyData.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

/// @dev The combination of target and selector is not authorized.
/// @param target Target address.
/// @param selector Function selector.
/// @param chainId Chain Id.
error NotAuthorized(address target, bytes4 selector, uint256 chainId);

/// @title VerifyData - Smart contract for verifying the Guard CM data
/// @author Aleksandr Kuperman - <[email protected]>
/// @author Andrey Lebedev - <[email protected]>
/// @author Mariapia Moscatiello - <[email protected]>
abstract contract VerifyData {
// Mapping of (target address | bytes4 selector | uint64 chain Id) => enabled / disabled
mapping(uint256 => bool) public mapAllowedTargetSelectorChainIds;

/// @dev Verifies authorized combinations of target and selector.
/// @notice The bottom-most internal function is still not "view" since some reverts are not explicitly handled
/// @param target Target address.
/// @param data Payload bytes.
/// @param chainId Chain Id.
function _verifyData(address target, bytes memory data, uint256 chainId) internal {
// Push a pair of key defining variables into one key
// target occupies first 160 bits
uint256 targetSelectorChainId = uint256(uint160(target));
// selector occupies next 32 bits
targetSelectorChainId |= uint256(uint32(bytes4(data))) << 160;
// chainId occupies next 64 bits
targetSelectorChainId |= chainId << 192;

// Check the authorized combination of target and selector
if (!mapAllowedTargetSelectorChainIds[targetSelectorChainId]) {
revert NotAuthorized(target, bytes4(data), chainId);
}
}
}
66 changes: 66 additions & 0 deletions contracts/multisigs/bridge_verifier/ProcessBridgedDataArbitrum.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

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

/// @dev Provided incorrect data length.
/// @param expected Expected minimum data length.
/// @param provided Provided data length.
error IncorrectDataLength(uint256 expected, uint256 provided);

/// @dev Provided wrong function selector.
/// @param functionSig Function selector.
/// @param chainId Chain Id.
error WrongSelector(bytes4 functionSig, uint256 chainId);

/// @dev Provided wrong L2 bridge mediator address.
/// @param provided Provided address.
/// @param expected Expected address.
error WrongL2BridgeMediator(address provided, address expected);

/// @title ProcessBridgedDataArbitrum - Smart contract for verifying the Guard CM bridged data on Arbitrum
/// @author Aleksandr Kuperman - <[email protected]>
/// @author Andrey Lebedev - <[email protected]>
/// @author Mariapia Moscatiello - <[email protected]>
contract ProcessBridgedDataArbitrum is VerifyBridgedData {
// unsafeCreateRetryableTicket selector in bridge mediator L1
bytes4 public constant CREATE_TICKET_UNSAFE = bytes4(keccak256(bytes("unsafeCreateRetryableTicket(address,uint256,uint256,address,address,uint256,uint256,uint256,bytes)")));
// createRetryableTicket selector in bridge mediator L1
bytes4 public constant CREATE_TICKET = bytes4(keccak256(bytes("createRetryableTicket(address,uint256,uint256,address,address,uint256,uint256,uint256,bytes)")));
// Minimum payload length for message on Arbitrum accounting for all required encoding and at least one selector
uint256 public constant MIN_ARBITRUM_PAYLOAD_LENGTH = 584;

/// @dev Processes bridged data: checks the header and verifies the payload.
/// @param data Full data bytes with the header.
/// @param chainId L2 chain Id.
function processBridgeData(
bytes memory data,
address,
uint256 chainId
) external override
{
// Check the L1 initial selector
bytes4 functionSig = bytes4(data);
if (functionSig != CREATE_TICKET_UNSAFE && functionSig != CREATE_TICKET) {
revert WrongSelector(functionSig, chainId);
}

// Check if the data length is less than a size of a selector plus the message minimum payload size
if (data.length < MIN_ARBITRUM_PAYLOAD_LENGTH) {
revert IncorrectDataLength(data.length, MIN_ARBITRUM_PAYLOAD_LENGTH);
}

// Copy the data without the selector
bytes memory payload = new bytes(data.length - SELECTOR_DATA_LENGTH);
for (uint256 i = 0; i < payload.length; ++i) {
payload[i] = data[i + 4];
}

// Decode the payload depending on the selector
(address targetAddress, , , , , , , , bytes memory targetPayload) =
abi.decode(payload, (address, uint256, uint256, address, address, uint256, uint256, uint256, bytes));

// Verify the scope of the data
_verifyData(targetAddress, targetPayload, chainId);
}
}
85 changes: 85 additions & 0 deletions contracts/multisigs/bridge_verifier/ProcessBridgedDataGnosis.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

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

/// @dev Provided incorrect data length.
/// @param expected Expected minimum data length.
/// @param provided Provided data length.
error IncorrectDataLength(uint256 expected, uint256 provided);

/// @dev Provided wrong function selector.
/// @param functionSig Function selector.
/// @param chainId Chain Id.
error WrongSelector(bytes4 functionSig, uint256 chainId);

/// @dev Provided wrong L2 bridge mediator address.
/// @param provided Provided address.
/// @param expected Expected address.
error WrongL2BridgeMediator(address provided, address expected);

/// @title ProcessBridgedDataGnosis - Smart contract for verifying the Guard CM bridged data on Gnosis
/// @author Aleksandr Kuperman - <[email protected]>
/// @author Andrey Lebedev - <[email protected]>
/// @author Mariapia Moscatiello - <[email protected]>
contract ProcessBridgedDataGnosis is VerifyBridgedData {
// requireToPassMessage selector in bridge mediator L1
bytes4 public constant REQUIRE_TO_PASS_MESSAGE = bytes4(keccak256(bytes("requireToPassMessage(address,bytes,uint256)")));
// processMessageFromForeign selector (Gnosis chain)
bytes4 public constant PROCESS_MESSAGE_FROM_FOREIGN = bytes4(keccak256(bytes("processMessageFromForeign(bytes)")));
// Minimum payload length for message on Gnosis accounting for all required encoding and at least one selector
uint256 public constant MIN_GNOSIS_PAYLOAD_LENGTH = 292;

/// @dev Processes bridged data: checks the header and verifies the payload.
/// @param data Full data bytes with the header.
/// @param bridgeMediatorL2 Address of a bridged mediator on L2.
/// @param chainId L2 chain Id.
function processBridgeData(
bytes memory data,
address bridgeMediatorL2,
uint256 chainId
) external override
{
// Check the L1 initial selector
bytes4 functionSig = bytes4(data);
if (functionSig != REQUIRE_TO_PASS_MESSAGE) {
revert WrongSelector(functionSig, chainId);
}

// Check if the data length is less than a size of a selector plus the message minimum payload size
if (data.length < MIN_GNOSIS_PAYLOAD_LENGTH) {
revert IncorrectDataLength(data.length, MIN_GNOSIS_PAYLOAD_LENGTH);
}

// Copy the data without the selector
bytes memory payload = new bytes(data.length - SELECTOR_DATA_LENGTH);
for (uint256 i = 0; i < payload.length; ++i) {
payload[i] = data[i + 4];
}

// Decode the requireToPassMessage payload: homeMediator (L2), mediatorPayload (needs decoding), requestGasLimit
(address homeMediator, bytes memory mediatorPayload, ) = abi.decode(payload, (address, bytes, uint256));
// Check that the home mediator matches the L2 bridge mediator address
if (homeMediator != bridgeMediatorL2) {
revert WrongL2BridgeMediator(homeMediator, bridgeMediatorL2);
}

// Check the L2 initial selector
functionSig = bytes4(mediatorPayload);
if (functionSig != PROCESS_MESSAGE_FROM_FOREIGN) {
revert WrongSelector(functionSig, chainId);
}

// Copy the data without a selector
bytes memory bridgePayload = new bytes(mediatorPayload.length - SELECTOR_DATA_LENGTH);
for (uint256 i = 0; i < bridgePayload.length; ++i) {
bridgePayload[i] = mediatorPayload[i + SELECTOR_DATA_LENGTH];
}

// Decode the processMessageFromForeign payload: l2Message (executed on L2)
(bytes memory l2Message) = abi.decode(bridgePayload, (bytes));

// Verify processMessageFromForeign payload
_verifyBridgedData(l2Message, chainId);
}
}
85 changes: 85 additions & 0 deletions contracts/multisigs/bridge_verifier/ProcessBridgedDataOptimism.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

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

/// @dev Provided incorrect data length.
/// @param expected Expected minimum data length.
/// @param provided Provided data length.
error IncorrectDataLength(uint256 expected, uint256 provided);

/// @dev Provided wrong function selector.
/// @param functionSig Function selector.
/// @param chainId Chain Id.
error WrongSelector(bytes4 functionSig, uint256 chainId);

/// @dev Provided wrong L2 bridge mediator address.
/// @param provided Provided address.
/// @param expected Expected address.
error WrongL2BridgeMediator(address provided, address expected);

/// @title ProcessBridgedDataOptimism - Smart contract for verifying the Guard CM bridged data on Optimism and Base
/// @author Aleksandr Kuperman - <[email protected]>
/// @author Andrey Lebedev - <[email protected]>
/// @author Mariapia Moscatiello - <[email protected]>
contract ProcessBridgedDataOptimism is VerifyBridgedData {
// sendMessage selector in bridge mediator L1
bytes4 public constant SEND_MESSAGE = bytes4(keccak256(bytes("sendMessage(address,bytes,uint32)")));
// processMessageFromSource selector (Optimism and Base chains)
bytes4 public constant PROCESS_MESSAGE_FROM_SOURCE = bytes4(keccak256(bytes("processMessageFromSource(bytes)")));
// Minimum payload length for message on Optimism accounting for all required encoding and at least one selector
uint256 public constant MIN_OPTIMISM_PAYLOAD_LENGTH = 264;

/// @dev Processes bridged data: checks the header and verifies the payload.
/// @param data Full data bytes with the header.
/// @param bridgeMediatorL2 Address of a bridged mediator on L2.
/// @param chainId L2 chain Id.
function processBridgeData(
bytes memory data,
address bridgeMediatorL2,
uint256 chainId
) external override
{
// Check the L1 initial selector
bytes4 functionSig = bytes4(data);
if (functionSig != SEND_MESSAGE) {
revert WrongSelector(functionSig, chainId);
}

// Check if the data length is less than a size of a selector plus the message minimum payload size
if (data.length < MIN_OPTIMISM_PAYLOAD_LENGTH) {
revert IncorrectDataLength(data.length, MIN_OPTIMISM_PAYLOAD_LENGTH);
}

// Copy the data without the selector
bytes memory payload = new bytes(data.length - SELECTOR_DATA_LENGTH);
for (uint256 i = 0; i < payload.length; ++i) {
payload[i] = data[i + 4];
}

// Decode the sendMessage payload: optimismMessenger (L2), mediatorPayload (needs decoding), minGasLimit
(address optimismMessenger, bytes memory mediatorPayload, ) = abi.decode(payload, (address, bytes, uint32));
// Check that the optimism messenger matches the L2 bridge mediator address
if (optimismMessenger != bridgeMediatorL2) {
revert WrongL2BridgeMediator(optimismMessenger, bridgeMediatorL2);
}

// Check the L2 initial selector
functionSig = bytes4(mediatorPayload);
if (functionSig != PROCESS_MESSAGE_FROM_SOURCE) {
revert WrongSelector(functionSig, chainId);
}

// Copy the data without a selector
bytes memory bridgePayload = new bytes(mediatorPayload.length - SELECTOR_DATA_LENGTH);
for (uint256 i = 0; i < bridgePayload.length; ++i) {
bridgePayload[i] = mediatorPayload[i + SELECTOR_DATA_LENGTH];
}

// Decode the processMessageFromSource payload: l2Message (executed on L2)
(bytes memory l2Message) = abi.decode(bridgePayload, (bytes));

// Verify processMessageFromSource payload
_verifyBridgedData(l2Message, chainId);
}
}
68 changes: 68 additions & 0 deletions contracts/multisigs/bridge_verifier/ProcessBridgedDataPolygon.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

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

/// @dev Provided incorrect data length.
/// @param expected Expected minimum data length.
/// @param provided Provided data length.
error IncorrectDataLength(uint256 expected, uint256 provided);

/// @dev Provided wrong function selector.
/// @param functionSig Function selector.
/// @param chainId Chain Id.
error WrongSelector(bytes4 functionSig, uint256 chainId);

/// @dev Provided wrong L2 bridge mediator address.
/// @param provided Provided address.
/// @param expected Expected address.
error WrongL2BridgeMediator(address provided, address expected);

/// @title ProcessBridgedDataPolygon - Smart contract for verifying the Guard CM bridged data on Polygon
/// @author Aleksandr Kuperman - <[email protected]>
/// @author Andrey Lebedev - <[email protected]>
/// @author Mariapia Moscatiello - <[email protected]>
contract ProcessBridgedDataPolygon is VerifyBridgedData {
// sendMessageToChild selector in bridge mediator L1
bytes4 public constant SEND_MESSAGE_TO_CHILD = bytes4(keccak256(bytes("sendMessageToChild(address,bytes)")));
// Minimum payload length for message on Polygon accounting for all required encoding and at least one selector
uint256 public constant MIN_POLYGON_PAYLOAD_LENGTH = 164;

/// @dev Processes bridged data: checks the header and verifies the payload.
/// @param data Full data bytes with the header.
/// @param bridgeMediatorL2 Address of a bridged mediator on L2.
/// @param chainId L2 chain Id.
function processBridgeData(
bytes memory data,
address bridgeMediatorL2,
uint256 chainId
) external override
{
// Check the L1 initial selector
bytes4 functionSig = bytes4(data);
if (functionSig != SEND_MESSAGE_TO_CHILD) {
revert WrongSelector(functionSig, chainId);
}

// Check if the data length is less than a size of a selector plus the message minimum payload size
if (data.length < MIN_POLYGON_PAYLOAD_LENGTH) {
revert IncorrectDataLength(data.length, MIN_POLYGON_PAYLOAD_LENGTH);
}

// Copy the data without the selector
bytes memory payload = new bytes(data.length - SELECTOR_DATA_LENGTH);
for (uint256 i = 0; i < payload.length; ++i) {
payload[i] = data[i + SELECTOR_DATA_LENGTH];
}

// Decode sendMessageToChild payload: fxGovernorTunnel (L2), l2Message (executed on L2)
(address fxGovernorTunnel, bytes memory l2Message) = abi.decode(payload, (address, bytes));
// Check that the fxGovernorTunnel matches the L2 bridge mediator address
if (fxGovernorTunnel != bridgeMediatorL2) {
revert WrongL2BridgeMediator(fxGovernorTunnel, bridgeMediatorL2);
}

// Verify sendMessageToChild payload
_verifyBridgedData(l2Message, chainId);
}
}
Loading
Loading