Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
12 changes: 12 additions & 0 deletions src/rollup/IRollupLogic.sol
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,16 @@ interface IRollupUser is IRollupCore, IOwnable {
address expectedWithdrawalAddress,
uint256 tokenAmount
) external;

function fastConfirmAssertion(
bytes32 assertionHash,
bytes32 parentAssertionHash,
AssertionState calldata confirmState,
bytes32 inboxAcc
) external;

function fastConfirmNewAssertion(
AssertionInputs calldata assertion,
bytes32 expectedAssertionHash
) external;
}
134 changes: 134 additions & 0 deletions src/rollup/ZeroLevelBoldFastConfirmer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Copyright 2021-2022, Offchain Labs, Inc.
// For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE
// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.0;

import {AssertionInputs} from "./Assertion.sol";
import {AssertionState, AssertionStateLib} from "./AssertionState.sol";
import {RollupLib} from "./RollupLib.sol";
import {
OwnableUpgradeable
} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {
EIP712Upgradeable
} from "@openzeppelin/contracts-upgradeable/utils/cryptography/draft-EIP712Upgradeable.sol";
import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol";
import {IRollupUser} from "./IRollupLogic.sol";

// dummy interface for the SNARK verifier.
interface ISnarkVerifier {
function verifyProof(
bytes32 assertionHash,
bytes calldata input
) external view returns (bool);
}

// todo: add AnyTrust fast confirmer path to perserve that functionality
contract ZeroLevelBoldFastConfirmer is OwnableUpgradeable, EIP712Upgradeable {
using AssertionStateLib for AssertionState;

bytes32 public constant FAST_CONFIRM_TYPEHASH =
keccak256("FastConfirmAssertion(bytes32 assertionHash)");

/// @notice The rollup contract to fast confirm on
IRollupUser public rollup;

/// @notice Gnosis Safe multisig address of the guardian council
address public guardianCouncil;

/// @notice SNARK verifier contract address
ISnarkVerifier public snarkVerifier;

error InvalidGuardianSignature();
error InvalidSnarkProof();
error LatestConfirmedAssertionMismatch(bytes32 expected, bytes32 actual);

event GuardianCouncilSet(address indexed newGuardianCouncil);

function initialize(
address _rollup,
address _guardianCouncil,
address _initialOwner,
address _snarkVerifier
) public initializer {
__Ownable_init();
__EIP712_init("ZeroLevelBoldFastConfirmer", "1");
rollup = IRollupUser(_rollup);
guardianCouncil = _guardianCouncil;
_transferOwnership(_initialOwner);
snarkVerifier = ISnarkVerifier(_snarkVerifier);
}

function fastConfirmAssertion(
bytes32 assertionHash,
bytes32 parentAssertionHash,
AssertionState calldata confirmState,
bytes32 inboxAcc,
bytes calldata guardianSignature,
bytes calldata snarkProof
) public {
_verifyProofs(assertionHash, guardianSignature, snarkProof);
_verifyLatestConfirmed(parentAssertionHash);
rollup.fastConfirmAssertion(assertionHash, parentAssertionHash, confirmState, inboxAcc);
}

function fastConfirmNewAssertion(
AssertionInputs calldata assertion,
bytes32 assertionHash,
bytes calldata guardianSignature,
bytes calldata snarkProof
) external {
_verifyProofs(assertionHash, guardianSignature, snarkProof);
_verifyLatestConfirmed(RollupLib.assertionHash(
assertion.beforeStateData.prevPrevAssertionHash,
assertion.beforeState,
assertion.beforeStateData.sequencerBatchAcc
));
rollup.fastConfirmNewAssertion(assertion, assertionHash);
}

function getFastConfirmAssertionMessageDigest(
bytes32 assertionHash
) public view returns (bytes32) {
return _hashTypedDataV4(keccak256(abi.encode(FAST_CONFIRM_TYPEHASH, assertionHash)));
}

function setGuardianCouncil(
address _guardianCouncil
) external onlyOwner {
guardianCouncil = _guardianCouncil;
emit GuardianCouncilSet(_guardianCouncil);
}

function setSnarkVerifier(
address _snarkVerifier
) external onlyOwner {
snarkVerifier = ISnarkVerifier(_snarkVerifier);
}

function _verifyLatestConfirmed(bytes32 parentAssertionHash) internal view {
bytes32 latestConfirmed = rollup.latestConfirmed();
if (parentAssertionHash != latestConfirmed) {
revert LatestConfirmedAssertionMismatch(parentAssertionHash, latestConfirmed);
}
}

function _verifyProofs(
bytes32 assertionHash,
bytes calldata guardianSignature,
bytes calldata snarkProof
) internal view {
bytes32 messageDigest = getFastConfirmAssertionMessageDigest(assertionHash);
if (
IERC1271(guardianCouncil).isValidSignature(messageDigest, guardianSignature)
!= 0x1626ba7e
) {
revert InvalidGuardianSignature();
}

if (!snarkVerifier.verifyProof(assertionHash, snarkProof)) {
revert InvalidSnarkProof();
}
}
}
Loading