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

System messaging #59

Merged
merged 9 commits into from
Jul 18, 2022
32 changes: 31 additions & 1 deletion packages/contracts/contracts/Home.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { MerkleLib } from "./libs/Merkle.sol";
import { Header } from "./libs/Header.sol";
import { Message } from "./libs/Message.sol";
import { Tips } from "./libs/Tips.sol";
import { SystemMessage } from "./system/SystemMessage.sol";
import { MerkleTreeManager } from "./Merkle.sol";
import { QueueManager } from "./Queue.sol";
import { IUpdaterManager } from "./interfaces/IUpdaterManager.sol";
Expand Down Expand Up @@ -206,9 +207,11 @@ contract Home is Version0, QueueManager, MerkleTreeManager, UpdaterStorage {
// get the next nonce for the destination domain, then increment it
uint32 _nonce = nonces[_destinationDomain];
nonces[_destinationDomain] = _nonce + 1;
bytes32 _sender = _checkForSystemMessage(_recipientAddress);
// format the message into packed bytes
bytes memory _header = Header.formatHeader(
localDomain,
TypeCasts.addressToBytes32(msg.sender),
_sender,
_nonce,
_destinationDomain,
_recipientAddress,
Expand Down Expand Up @@ -413,4 +416,31 @@ contract Home is Version0, QueueManager, MerkleTreeManager, UpdaterStorage {
{
return (uint64(_destination) << 32) | _nonce;
}

/**
* @notice Returns "adjusted" sender address.
* @dev By default, "sender address" is msg.sender.
* However, if SystemMessenger sends a message, specifying SYSTEM_SENDER as the recipient,
* SYSTEM_SENDER is used as "sender address" on origin chain.
* Note that transaction will revert if anyone but SystemMessenger uses SYSTEM_SENDER as the recipient.
*/
function _checkForSystemMessage(bytes32 _recipientAddress)
internal
view
returns (bytes32 sender)
{
if (_recipientAddress != SystemMessage.SYSTEM_SENDER) {
sender = TypeCasts.addressToBytes32(msg.sender);
/**
* @dev Note: SYSTEM_SENDER has highest 12 bytes set,
* whereas TypeCasts.addressToBytes32 sets only the lowest 20 bytes.
* Thus, in this branch: sender != SystemMessage.SYSTEM_SENDER
*/
} else {
// Check that SystemMessenger specified SYSTEM_SENDER as recipient, revert otherwise.
_assertSystemMessenger();
// Adjust "sender address" for correct processing on remote chain.
sender = SystemMessage.SYSTEM_SENDER;
}
}
}
30 changes: 26 additions & 4 deletions packages/contracts/contracts/ReplicaManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { MerkleLib } from "./libs/Merkle.sol";
import { Message } from "./libs/Message.sol";
import { Header } from "./libs/Header.sol";
import { Tips } from "./libs/Tips.sol";
import { TypeCasts } from "./libs/TypeCasts.sol";
import { SystemMessage } from "./system/SystemMessage.sol";
import { IMessageRecipient } from "./interfaces/IMessageRecipient.sol";
// ============ External Imports ============
import { TypedMemView } from "./libs/TypedMemView.sol";
Expand Down Expand Up @@ -139,6 +141,8 @@ contract ReplicaManager is Version0, UpdaterStorage {
bytes memory _signature
) external {
ReplicaLib.Replica storage replica = allReplicas[activeReplicas[_remoteDomain]];
// ensure that replica is active
require(replica.status == ReplicaLib.ReplicaStatus.Active, "Replica not active");
// ensure that update is building off the last submitted root
require(_oldRoot == replica.committedRoot, "not current update");
// validate updater signature
Expand Down Expand Up @@ -201,7 +205,7 @@ contract ReplicaManager is Version0, UpdaterStorage {
_storeTips(_m.tips());
// update message status as processed
replica.setMessageStatus(_messageHash, ReplicaLib.MESSAGE_STATUS_PROCESSED);
address recipient = _header.recipientAddress();
address recipient = _checkForSystemMessage(_header.recipient());
IMessageRecipient(recipient).handle(
_remoteDomain,
_header.nonce(),
Expand Down Expand Up @@ -293,6 +297,8 @@ contract ReplicaManager is Version0, UpdaterStorage {
) public returns (bool) {
bytes32 _leaf = keccak256(_message);
ReplicaLib.Replica storage replica = allReplicas[activeReplicas[_remoteDomain]];
// ensure that replica is active
require(replica.status == ReplicaLib.ReplicaStatus.Active, "Replica not active");
// ensure that message has not been proven or processed
require(
replica.messageStatus[_leaf] == ReplicaLib.MESSAGE_STATUS_NONE,
Expand All @@ -311,11 +317,12 @@ contract ReplicaManager is Version0, UpdaterStorage {
// ============ Internal Functions ============

function _createReplica(uint32 _remoteDomain) internal returns (uint256 replicaIndex) {
replicaIndex = replicaCount;
allReplicas[replicaIndex].setupReplica(_remoteDomain);
// Start indexing from 1, so default replica (allReplicas[0]) will be forever inactive
unchecked {
replicaCount = replicaIndex + 1;
replicaIndex = replicaCount + 1;
}
allReplicas[replicaIndex].setupReplica(_remoteDomain);
replicaCount = replicaIndex;
}

/// @notice Hook for potential future use
Expand All @@ -333,6 +340,21 @@ contract ReplicaManager is Version0, UpdaterStorage {
return abi.decode(_returnData, (string)); // All that remains is the revert string
}

function _checkForSystemMessage(bytes32 _recipient) internal view returns (address recipient) {
// Check if SYSTEM_SENDER was specified as message recipient
if (_recipient == SystemMessage.SYSTEM_SENDER) {
/**
* @dev Route message to SystemMessenger.
* Note: Only SystemMessenger contract on origin chain
* can send such a message (enforced in Home.sol).
*/
recipient = address(systemMessenger);
} else {
// Cast bytes32 to address otherwise
recipient = TypeCasts.bytes32ToAddress(_recipient);
}
}

function _storeTips(bytes29 _tips) internal virtual {
// TODO: implement storing & claiming logic
}
Expand Down
26 changes: 25 additions & 1 deletion packages/contracts/contracts/UpdaterStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pragma solidity 0.8.13;

// ============ Internal Imports ============
import { ISystemMessenger } from "./interfaces/ISystemMessenger.sol";
import { Message } from "./libs/Message.sol";
// ============ External Imports ============
import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
Expand All @@ -26,10 +27,12 @@ abstract contract UpdaterStorage is Initializable, OwnableUpgradeable {
// Address of bonded Updater
address public updater;

ISystemMessenger public systemMessenger;

// ============ Upgrade Gap ============

// gap for upgrade safety
uint256[49] private __GAP;
uint256[48] private __GAP;

// ============ Events ============

Expand Down Expand Up @@ -68,8 +71,29 @@ abstract contract UpdaterStorage is Initializable, OwnableUpgradeable {
_setUpdater(_updater);
}

// ============ Modifiers ============

/**
* @dev Modifier for functions that are supposed to be called from
* System Contracts on other chains.
*/
modifier onlySystemMessenger() {
_assertSystemMessenger();
_;
}

// ============ Restricted Functions ============

function setSystemMessenger(ISystemMessenger _systemMessenger) external onlyOwner {
systemMessenger = _systemMessenger;
}

// ============ Internal Functions ============

function _assertSystemMessenger() internal view {
require(msg.sender == address(systemMessenger), "!systemMessenger");
}

/**
* @notice Hash of domain concatenated with "SYN"
* @param _domain The domain to hash
Expand Down
24 changes: 24 additions & 0 deletions packages/contracts/contracts/interfaces/ISystemMessenger.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

interface ISystemMessenger {
/// @dev Potential senders/recipients of a system message
enum SystemContracts {
Home,
ReplicaManager
}

/**
* @notice Send System Message to one of the System Contracts on origin chain
* @dev Note that knowledge of recipient address is not required,
* routing will be done by SystemMessenger on destination chain.
* @param _destDomain Domain of destination chain
* @param _recipient System contract type of the recipient
* @param _payload Data for calling recipient on destination chain
*/
function sendSystemMessage(
uint32 _destDomain,
SystemContracts _recipient,
bytes memory _payload
) external;
}
106 changes: 106 additions & 0 deletions packages/contracts/contracts/system/SystemMessage.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

import { TypedMemView } from "../libs/TypedMemView.sol";

library SystemMessage {
using TypedMemView for bytes29;

enum SystemMessageType {
None,
Call,
Adjust
}

/**
* @dev Custom address, used for receiving and sending system messages.
* Home is supposed to dispatch messages from SystemMessenger as if they were sent by this address.
* ReplicaManager is supposed to reroute messages for this address to SystemMessenger.
* Note: all bits except for lower 20 bytes are set to 1.
* Note: TypeCasts.bytes32ToAddress(SYSTEM_SENDER) = address(0)
*/
bytes32 internal constant SYSTEM_SENDER = bytes32(type(uint256).max << 160);

/**
* @dev SystemMessage memory layout
* [000 .. 001): messageType uint8 1 bytes
* [001 .. END]: messageBody bytes ? bytes
*/

uint256 internal constant OFFSET_BODY = 1;

/**
* @dev SystemMessageType.Call memory layout
* [000 .. 001): recipient uint8 1 bytes
* [001 .. END]: payload bytes ? bytes
*/

uint256 internal constant OFFSET_CALL_PAYLOAD = 1;

// TODO: memory layout + setter/getters for SystemMessageType.Adjust

modifier onlyType(SystemMessageType _type, bytes29 _view) {
_view.assertType(uint40(_type));
_;
}

/*╔══════════════════════════════════════════════════════════════════════╗*\
▏*║ FORMATTERS ║*▕
\*╚══════════════════════════════════════════════════════════════════════╝*/

function formatSystemMessage(SystemMessageType _messageType, bytes memory _messageBody)
internal
pure
returns (bytes memory)
{
return abi.encodePacked(uint8(_messageType), _messageBody);
}

function formatCall(uint8 _recipientType, bytes memory _payload)
internal
pure
returns (bytes memory)
{
return
formatSystemMessage(SystemMessageType.Call, abi.encodePacked(_recipientType, _payload));
}

/*╔══════════════════════════════════════════════════════════════════════╗*\
▏*║ SYSTEM MESSAGE GETTERS ║*▕
\*╚══════════════════════════════════════════════════════════════════════╝*/

function systemMessage(bytes29 _message)
internal
pure
returns (SystemMessageType _messageType, bytes29 _messageView)
{
_messageType = SystemMessageType(_message.indexUint(0, 1));
_messageView = _message.slice(
OFFSET_BODY,
_message.len() - OFFSET_BODY,
uint40(_messageType)
);
}

/*╔══════════════════════════════════════════════════════════════════════╗*\
▏*║ SYSTEM_MESSAGE_TYPE.CALL GETTERS ║*▕
\*╚══════════════════════════════════════════════════════════════════════╝*/

function callRecipient(bytes29 _call)
internal
pure
onlyType(SystemMessageType.Call, _call)
returns (uint8)
{
return uint8(_call.indexUint(0, 1));
}

function callPayload(bytes29 _call)
internal
pure
onlyType(SystemMessageType.Call, _call)
returns (bytes29)
{
return _call.slice(OFFSET_CALL_PAYLOAD, _call.len() - OFFSET_CALL_PAYLOAD, 0);
}
}
Loading