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

Dispatch message with tips attached #53

Merged
merged 12 commits into from
Jul 18, 2022
23 changes: 17 additions & 6 deletions packages/contracts/contracts/Home.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ import { Version0 } from "./Version0.sol";
import { UpdaterStorage } from "./UpdaterStorage.sol";
import { QueueLib } from "./libs/Queue.sol";
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 { MerkleTreeManager } from "./Merkle.sol";
import { QueueManager } from "./Queue.sol";
import { IUpdaterManager } from "./interfaces/IUpdaterManager.sol";
import { TypeCasts } from "./libs/TypeCasts.sol";
// ============ External Imports ============
import { Address } from "@openzeppelin/contracts/utils/Address.sol";

Expand All @@ -29,6 +32,9 @@ contract Home is Version0, QueueManager, MerkleTreeManager, UpdaterStorage {
using QueueLib for QueueLib.Queue;
using MerkleLib for MerkleLib.Tree;

using Tips for bytes;
using Tips for bytes29;

// ============ Enums ============

// States:
Expand Down Expand Up @@ -76,13 +82,15 @@ contract Home is Version0, QueueManager, MerkleTreeManager, UpdaterStorage {
* nonce combined in single field ((destination << 32) & nonce)
* @param committedRoot the latest notarized root submitted in the last
* signed Update
* @param tips Tips paid for the remote off-chain agents
* @param message Raw bytes of message
*/
event Dispatch(
bytes32 indexed messageHash,
uint256 indexed leafIndex,
uint64 indexed destinationAndNonce,
bytes32 committedRoot,
bytes tips,
bytes message
);

Expand Down Expand Up @@ -190,22 +198,24 @@ contract Home is Version0, QueueManager, MerkleTreeManager, UpdaterStorage {
uint32 _destinationDomain,
bytes32 _recipientAddress,
uint32 _optimisticSeconds,
bytes memory _tips,
bytes memory _messageBody
) external notFailed {
) external payable notFailed {
require(_messageBody.length <= MAX_MESSAGE_BODY_BYTES, "msg too long");
require(_tips.tipsView().totalTips() == msg.value, "!tips");
// get the next nonce for the destination domain, then increment it
uint32 _nonce = nonces[_destinationDomain];
nonces[_destinationDomain] = _nonce + 1;
// format the message into packed bytes
bytes memory _message = Message.formatMessage(
bytes memory _header = Header.formatHeader(
localDomain,
bytes32(uint256(uint160(msg.sender))),
TypeCasts.addressToBytes32(msg.sender),
_nonce,
_destinationDomain,
_recipientAddress,
_optimisticSeconds,
_messageBody
_optimisticSeconds
);
// format the message into packed bytes
bytes memory _message = Message.formatMessage(_header, _tips, _messageBody);
// insert the hashed message into the Merkle tree
bytes32 _messageHash = keccak256(_message);
tree.insert(_messageHash);
Expand All @@ -218,6 +228,7 @@ contract Home is Version0, QueueManager, MerkleTreeManager, UpdaterStorage {
count() - 1,
_destinationAndNonce(_destinationDomain, _nonce),
committedRoot,
_tips,
_message
);
}
Expand Down
48 changes: 30 additions & 18 deletions packages/contracts/contracts/ReplicaManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { Version0 } from "./Version0.sol";
import { ReplicaLib } from "./libs/Replica.sol";
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 { IMessageRecipient } from "./interfaces/IMessageRecipient.sol";
// ============ External Imports ============
import { TypedMemView } from "./libs/TypedMemView.sol";
Expand All @@ -21,9 +23,10 @@ contract ReplicaManager is Version0, UpdaterStorage {

using ReplicaLib for ReplicaLib.Replica;
using MerkleLib for MerkleLib.Tree;
using TypedMemView for bytes;
using Message for bytes;
using TypedMemView for bytes29;
using Message for bytes29;
using Header for bytes29;

// ============ Public Storage ============

Expand Down Expand Up @@ -99,17 +102,17 @@ contract ReplicaManager is Version0, UpdaterStorage {
}

function activeReplicaConfirmedAt(uint32 _remoteDomain, bytes32 _root)
external
view
returns (uint256)
external
view
returns (uint256)
{
return allReplicas[activeReplicas[_remoteDomain]].confirmAt[_root];
}

function activeReplicaMessageStatus(uint32 _remoteDomain, bytes32 _messageId)
external
view
returns (bytes32)
external
view
returns (bytes32)
{
return allReplicas[activeReplicas[_remoteDomain]].messageStatus[_messageId];
}
Expand Down Expand Up @@ -178,26 +181,31 @@ contract ReplicaManager is Version0, UpdaterStorage {
* @param _message Formatted message
*/
function process(bytes memory _message) public {
bytes29 _m = _message.ref(0);
uint32 _remoteDomain = _m.origin();
bytes29 _m = _message.messageView();
bytes29 _header = _m.header();
uint32 _remoteDomain = _header.origin();
ReplicaLib.Replica storage replica = allReplicas[activeReplicas[_remoteDomain]];
// ensure message was meant for this domain
require(_m.destination() == localDomain, "!destination");
require(_header.destination() == localDomain, "!destination");
// ensure message has been proven
bytes32 _messageHash = _m.keccak();
bytes32 _root = replica.messageStatus[_messageHash];
require(ReplicaLib.isPotentialRoot(_root), "!exists || processed");
require(acceptableRoot(_remoteDomain, _m.optimisticSeconds(), _root), "!optimisticSeconds");
require(
acceptableRoot(_remoteDomain, _header.optimisticSeconds(), _root),
"!optimisticSeconds"
);
// check re-entrancy guard
require(entered == 1, "!reentrant");
entered = 0;
_storeTips(_m.tips());
// update message status as processed
replica.setMessageStatus(_messageHash, ReplicaLib.MESSAGE_STATUS_PROCESSED);
address recipient = _m.recipientAddress();
address recipient = _header.recipientAddress();
IMessageRecipient(recipient).handle(
_remoteDomain,
_m.nonce(),
_m.sender(),
_header.nonce(),
_header.sender(),
replica.confirmAt[_root],
_m.body().clone()
);
Expand Down Expand Up @@ -305,9 +313,9 @@ contract ReplicaManager is Version0, UpdaterStorage {
function _createReplica(uint32 _remoteDomain) internal returns (uint256 replicaIndex) {
replicaIndex = replicaCount;
allReplicas[replicaIndex].setupReplica(_remoteDomain);
unchecked {
replicaCount = replicaIndex + 1;
}
unchecked {
replicaCount = replicaIndex + 1;
}
}

/// @notice Hook for potential future use
Expand All @@ -319,9 +327,13 @@ contract ReplicaManager is Version0, UpdaterStorage {
if (_returnData.length < 68) return "Transaction reverted silently";

assembly {
// Slice the sighash.
// Slice the sighash.
_returnData := add(_returnData, 0x04)
}
return abi.decode(_returnData, (string)); // All that remains is the revert string
}

function _storeTips(bytes29 _tips) internal virtual {
// TODO: implement storing & claiming logic
}
}
14 changes: 12 additions & 2 deletions packages/contracts/contracts/client/Client.sol
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,20 @@ abstract contract Client is IMessageRecipient {
* @param _destination Domain of the destination chain
* @param _message The message
*/
function _send(uint32 _destination, bytes memory _message) internal {
function _send(
uint32 _destination,
bytes memory _tips,
bytes memory _message
) internal {
bytes32 recipient = trustedSender(_destination);
require(recipient != bytes32(0), "Client: !recipient");
Home(home).dispatch(_destination, recipient, optimisticSeconds(), _message);
Home(home).dispatch{ value: msg.value }(
_destination,
recipient,
optimisticSeconds(),
_tips,
_message
);
}

/// @dev Period of time since the root was submitted to Replica. Once this period is over,
Expand Down
102 changes: 102 additions & 0 deletions packages/contracts/contracts/libs/Header.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

import { TypedMemView } from "./TypedMemView.sol";
import { TypeCasts } from "./TypeCasts.sol";
import { Message } from "./Message.sol";

/**
* @notice Library for versioned formatting [the header part] of [the messages used by Home and Replicas].
*/
library Header {
using TypedMemView for bytes;
using TypedMemView for bytes29;

uint16 internal constant HEADER_VERSION = 1;

/**
* @dev Header memory layout
* [000 .. 002): version uint16 2 bytes
* [002 .. 006): originDomain uint32 4 bytes
* [006 .. 038): sender bytes32 32 bytes
* [038 .. 042): nonce uint32 4 bytes
* [042 .. 046): destinationDomain uint32 4 bytes
* [046 .. 078): recipient bytes32 32 bytes
* [078 .. 082): optimisticSeconds uint32 4 bytes
*/

uint256 private constant OFFSET_ORIGIN = 2;
uint256 private constant OFFSET_SENDER = 6;
uint256 private constant OFFSET_NONCE = 38;
uint256 private constant OFFSET_DESTINATION = 42;
uint256 private constant OFFSET_RECIPIENT = 46;
uint256 private constant OFFSET_OPTIMISTIC_SECONDS = 78;

modifier onlyHeader(bytes29 _view) {
_view.assertType(Message.HEADER_TYPE);
_;
}

function formatHeader(
uint32 _originDomain,
bytes32 _sender,
uint32 _nonce,
uint32 _destinationDomain,
bytes32 _recipient,
uint32 _optimisticSeconds
) internal pure returns (bytes memory) {
return
abi.encodePacked(
HEADER_VERSION,
_originDomain,
_sender,
_nonce,
_destinationDomain,
_recipient,
_optimisticSeconds
);
}

function headerView(bytes memory _header) internal pure returns (bytes29) {
return _header.ref(Message.HEADER_TYPE);
}

function headerVersion(bytes29 _header) internal pure onlyHeader(_header) returns (uint16) {
return uint16(_header.indexUint(0, 2));
}

/// @notice Returns header's origin field
function origin(bytes29 _header) internal pure onlyHeader(_header) returns (uint32) {
return uint32(_header.indexUint(OFFSET_ORIGIN, 4));
}

/// @notice Returns header's sender field
function sender(bytes29 _header) internal pure onlyHeader(_header) returns (bytes32) {
return _header.index(OFFSET_SENDER, 32);
}

/// @notice Returns header's nonce field
function nonce(bytes29 _header) internal pure onlyHeader(_header) returns (uint32) {
return uint32(_header.indexUint(OFFSET_NONCE, 4));
}

/// @notice Returns header's destination field
function destination(bytes29 _header) internal pure onlyHeader(_header) returns (uint32) {
return uint32(_header.indexUint(OFFSET_DESTINATION, 4));
}

/// @notice Returns header's recipient field as bytes32
function recipient(bytes29 _header) internal pure onlyHeader(_header) returns (bytes32) {
return _header.index(OFFSET_RECIPIENT, 32);
}

/// @notice Returns header's optimistic seconds field
function optimisticSeconds(bytes29 _header) internal pure onlyHeader(_header) returns (uint32) {
return uint32(_header.indexUint(OFFSET_OPTIMISTIC_SECONDS, 4));
}

/// @notice Returns header's recipient field as an address
function recipientAddress(bytes29 _header) internal pure returns (address) {
return TypeCasts.bytes32ToAddress(recipient(_header));
}
}
Loading