diff --git a/src/rollup/IRollupLogic.sol b/src/rollup/IRollupLogic.sol index b3f825fd..f61b1a1b 100644 --- a/src/rollup/IRollupLogic.sol +++ b/src/rollup/IRollupLogic.sol @@ -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; } diff --git a/src/rollup/ZeroLevelBoldFastConfirmer.sol b/src/rollup/ZeroLevelBoldFastConfirmer.sol new file mode 100644 index 00000000..6c3496f4 --- /dev/null +++ b/src/rollup/ZeroLevelBoldFastConfirmer.sol @@ -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(); + } + } +}