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

Remove caching of checkpoints from the Inbox #523

Merged
merged 14 commits into from
Jun 3, 2022
52 changes: 16 additions & 36 deletions solidity/core/contracts/Inbox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,8 @@ contract Inbox is IInbox, ReentrancyGuardUpgradeable, Version0, Mailbox {
* @dev This event allows watchers to observe the merkle proof they need
* to prove fraud on the Outbox.
* @param messageHash Hash of message that was processed.
* @param leafIndex The leaf index of the message that was processed.
* @param proof A merkle proof of inclusion of `messageHash` at `leafIndex`.
*/
event Process(
bytes32 indexed messageHash,
uint256 indexed leafIndex,
bytes32[32] proof
);
event Process(bytes32 indexed messageHash);

// ============ Constructor ============

Expand All @@ -82,41 +76,27 @@ contract Inbox is IInbox, ReentrancyGuardUpgradeable, Version0, Mailbox {

// ============ External Functions ============

/**
* @notice Caches the provided merkle root and index.
* @dev Called by the validator manager, which is responsible for verifying a
* quorum of validator signatures on the checkpoint.
* @dev Reverts if the checkpoint's index is not greater than the index of the latest checkpoint in the cache.
* @param _root Checkpoint's merkle root.
* @param _index Checkpoint's index.
*/
function cacheCheckpoint(bytes32 _root, uint256 _index)
external
override
onlyValidatorManager
{
// Ensure that the checkpoint is newer than the latest we've cached.
require(_index > cachedCheckpoints[latestCachedRoot], "!newer");
_cacheCheckpoint(_root, _index);
}

/**
* @notice Attempts to process the provided formatted `message`. Performs
* verification against root of the proof
* @dev Called by the validator manager, which is responsible for verifying a
* quorum of validator signatures on the checkpoint.
* @dev Reverts if verification of the message fails.
* @dev Includes the eventual function signature for Sovereign Consensus,
* but comments out the name to suppress compiler warning
* @param _root The merkle root of the checkpoint used to prove message inclusion.
* @param _index The index of the checkpoint used to prove message inclusion.
asaj marked this conversation as resolved.
Show resolved Hide resolved
asaj marked this conversation as resolved.
Show resolved Hide resolved
* @param _message Formatted message (refer to Mailbox.sol Message library)
* @param _proof Merkle proof of inclusion for message's leaf
* @param _index Index of leaf in outbox's merkle tree
* @param _leafIndex Index of leaf in outbox's merkle tree
*/
function process(
bytes32 _root,
uint256 _index,
bytes calldata _message,
bytes32[32] calldata _proof,
uint256 _index,
bytes calldata /* _sovereignData */
) external override nonReentrant {
bytes32 _messageHash = _message.leaf(_index);
uint256 _leafIndex
) external override nonReentrant onlyValidatorManager {
require(_index >= _leafIndex, "!index");
bytes32 _messageHash = _message.leaf(_leafIndex);
// ensure that message has not been processed
require(
messages[_messageHash] == MessageStatus.None,
Expand All @@ -126,12 +106,12 @@ contract Inbox is IInbox, ReentrancyGuardUpgradeable, Version0, Mailbox {
bytes32 _calculatedRoot = MerkleLib.branchRoot(
_messageHash,
_proof,
_index
_leafIndex
);
// ensure that the root has been cached
require(cachedCheckpoints[_calculatedRoot] >= _index, "!cache");
// verify the merkle proof
require(_calculatedRoot == _root, "!proof");
_process(_message, _messageHash);
emit Process(_messageHash, _index, _proof);
emit Process(_messageHash);
}

// ============ Internal Functions ============
Expand Down
43 changes: 1 addition & 42 deletions solidity/core/contracts/Mailbox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,29 +20,16 @@ abstract contract Mailbox is IMailbox, OwnableUpgradeable {

// ============ Public Variables ============

// Cached checkpoints, mapping root => leaf index.
// Cached checkpoints must have index > 0 as the presence of such
// a checkpoint cannot be distinguished from its absence.
mapping(bytes32 => uint256) public cachedCheckpoints;
// The latest cached root
bytes32 public latestCachedRoot;
// Address of the validator manager contract.
address public validatorManager;

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

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

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

/**
* @notice Emitted when a checkpoint is cached.
* @param root Merkle root
* @param index Leaf index
*/
event CheckpointCached(bytes32 indexed root, uint256 indexed index);

/**
* @notice Emitted when the validator manager contract is changed
* @param validatorManager The address of the new validatorManager
Expand Down Expand Up @@ -89,21 +76,6 @@ abstract contract Mailbox is IMailbox, OwnableUpgradeable {
_setValidatorManager(_validatorManager);
}

/**
* @notice Returns the latest entry in the checkpoint cache.
* @return root Latest cached root
* @return index Latest cached index
*/
function latestCachedCheckpoint()
external
view
override
returns (bytes32 root, uint256 index)
{
root = latestCachedRoot;
index = cachedCheckpoints[root];
}

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

/**
Expand All @@ -118,17 +90,4 @@ abstract contract Mailbox is IMailbox, OwnableUpgradeable {
validatorManager = _validatorManager;
emit NewValidatorManager(_validatorManager);
}

/**
* @notice Caches the provided checkpoint.
* Caching checkpoints with index == 0 are disallowed.
* @param _root The merkle root to cache.
* @param _index The leaf index of the latest message in the merkle tree.
*/
function _cacheCheckpoint(bytes32 _root, uint256 _index) internal {
require(_index > 0, "!index");
cachedCheckpoints[_root] = _index;
latestCachedRoot = _root;
emit CheckpointCached(_root, _index);
}
}
38 changes: 34 additions & 4 deletions solidity/core/contracts/Outbox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,29 @@ contract Outbox is IOutbox, Version0, MerkleTreeManager, Mailbox {

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

// Cached checkpoints, mapping root => leaf index.
// Cached checkpoints must have index > 0 as the presence of such
// a checkpoint cannot be distinguished from its absence.
mapping(bytes32 => uint256) public cachedCheckpoints;
// The latest cached root
bytes32 public latestCachedRoot;
// Current state of contract
States public state;

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

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

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

/**
* @notice Emitted when a checkpoint is cached.
* @param root Merkle root
* @param index Leaf index
*/
event CheckpointCached(bytes32 indexed root, uint256 indexed index);

/**
* @notice Emitted when a new message is dispatched via Abacus
* @param messageHash Hash of message; the leaf inserted to the Merkle tree for the message
Expand Down Expand Up @@ -134,11 +147,14 @@ contract Outbox is IOutbox, Version0, MerkleTreeManager, Mailbox {

/**
* @notice Caches the current merkle root and index.
* @dev emits Checkpoint event
* @dev emits CheckpointCached event
*/
function cacheCheckpoint() external override notFailed {
(bytes32 root, uint256 index) = latestCheckpoint();
_cacheCheckpoint(root, index);
(bytes32 _root, uint256 _index) = latestCheckpoint();
require(_index > 0, "!index");
cachedCheckpoints[_root] = _index;
latestCachedRoot = _root;
emit CheckpointCached(_root, _index);
}

/**
Expand All @@ -151,6 +167,20 @@ contract Outbox is IOutbox, Version0, MerkleTreeManager, Mailbox {
emit Fail();
}

/**
* @notice Returns the latest entry in the checkpoint cache.
* @return root Latest cached root
* @return index Latest cached index
*/
function latestCachedCheckpoint()
external
view
returns (bytes32 root, uint256 index)
{
root = latestCachedRoot;
index = cachedCheckpoints[root];
}

/**
* @notice Returns the number of inserted leaves in the tree
*/
Expand Down
4 changes: 0 additions & 4 deletions solidity/core/contracts/test/TestInbox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@ contract TestInbox is Inbox {

constructor(uint32 _localDomain) Inbox(_localDomain) {} // solhint-disable-line no-empty-blocks

function setCachedCheckpoint(bytes32 _root, uint256 _index) external {
cachedCheckpoints[_root] = _index;
}

function testBranchRoot(
bytes32 leaf,
bytes32[32] calldata proof,
Expand Down
4 changes: 0 additions & 4 deletions solidity/core/contracts/test/TestMailbox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,4 @@ contract TestMailbox is Mailbox {
function initialize(address _validatorManager) external initializer {
__Mailbox_initialize(_validatorManager);
}

function cacheCheckpoint(bytes32 _root, uint256 _index) external {
_cacheCheckpoint(_root, _index);
}
}
9 changes: 6 additions & 3 deletions solidity/core/contracts/test/TestValidatorManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ import {IInbox} from "../../interfaces/IInbox.sol";
* to be a contract.
*/
contract TestValidatorManager {
function cacheCheckpoint(
function process(
IInbox _inbox,
bytes32 _root,
uint256 _index
uint256 _index,
bytes calldata _message,
asaj marked this conversation as resolved.
Show resolved Hide resolved
bytes32[32] calldata _proof,
uint256 _leafIndex
) external {
_inbox.cacheCheckpoint(_root, _index);
_inbox.process(_root, _index, _message, _proof, _leafIndex);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;

contract BadRecipientHandle {
contract BadRecipient2 {
function handle(uint32, bytes32) external pure {} // solhint-disable-line no-empty-blocks
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,6 @@ import {MultisigValidatorManager} from "./MultisigValidatorManager.sol";
* them to an Inbox.
*/
contract InboxValidatorManager is MultisigValidatorManager {
// ============ Events ============

/**
* @notice Emitted when a checkpoint has been signed by a quorum
* of validators and cached on an Inbox.
* @dev This event allows watchers to observe the signatures they need
* to prove fraud on the Outbox.
asaj marked this conversation as resolved.
Show resolved Hide resolved
* @param signatures The signatures by a quorum of validators on the
* checkpoint.
*/
event Quorum(bytes[] signatures);

// ============ Constructor ============

/**
Expand All @@ -43,24 +31,29 @@ contract InboxValidatorManager is MultisigValidatorManager {
// ============ External Functions ============

/**
* @notice Submits a checkpoint signed by a quorum of validators to be cached by an Inbox.
* @notice Verifies a signed checkpoint and submits a message for processing.
* @dev Reverts if `_signatures` is not a quorum of validator signatures.
* @dev Reverts if `_signatures` is not sorted in ascending order by the signer
* address, which is required for duplicate detection.
* @param _inbox The inbox to submit the checkpoint to.
* @param _root The merkle root of the checkpoint.
* @param _index The index of the checkpoint.
* @param _inbox The inbox to submit the message to.
* @param _root The merkle root of the signed checkpoint.
* @param _index The index of the signed checkpoint.
* @param _signatures Signatures over the checkpoint to be checked for a validator
* quorum. Must be sorted in ascending order by signer address.
* @param _message The message to process.
* @param _proof Merkle proof of inclusion for message's leaf
* @param _leafIndex Index of leaf in outbox's merkle tree
*/
function cacheCheckpoint(
function process(
IInbox _inbox,
bytes32 _root,
uint256 _index,
bytes[] calldata _signatures
bytes[] calldata _signatures,
bytes calldata _message,
bytes32[32] calldata _proof,
uint256 _leafIndex
) external {
require(isQuorum(_root, _index, _signatures), "!quorum");
emit Quorum(_signatures);
_inbox.cacheCheckpoint(_root, _index);
_inbox.process(_root, _index, _message, _proof, _leafIndex);
}
}
7 changes: 3 additions & 4 deletions solidity/core/interfaces/IInbox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@ pragma solidity >=0.6.11;
import {IMailbox} from "./IMailbox.sol";

interface IInbox is IMailbox {
function cacheCheckpoint(bytes32 _root, uint256 _index) external;

function remoteDomain() external returns (uint32);

function process(
bytes32 _root,
uint256 _index,
bytes calldata _message,
bytes32[32] calldata _proof,
uint256 _index,
bytes calldata _sovereignData
uint256 _leafIndex
) external;
}
7 changes: 0 additions & 7 deletions solidity/core/interfaces/IMailbox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,4 @@ pragma solidity >=0.6.11;

interface IMailbox {
function localDomain() external view returns (uint32);

function cachedCheckpoints(bytes32) external view returns (uint256);

function latestCachedCheckpoint()
external
view
returns (bytes32 root, uint256 index);
}
7 changes: 7 additions & 0 deletions solidity/core/interfaces/IOutbox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,11 @@ interface IOutbox is IMailbox {
function count() external returns (uint256);

function fail() external;

function cachedCheckpoints(bytes32) external view returns (uint256);

function latestCachedCheckpoint()
external
view
returns (bytes32 root, uint256 index);
}
Loading