diff --git a/.env.sample.goerli b/.env.sample.goerli index e105b7790..68ebff264 100644 --- a/.env.sample.goerli +++ b/.env.sample.goerli @@ -7,4 +7,22 @@ INFURA_KEY="" ## optional - address of already deployed ERC20 token which shall be used as rollup's fee token FEE_TOKEN_ADDRESS="" -ESPRESSO_LIGHT_CLIENT_ADDRESS="" + +### Example TEE ATTESTATION CONTRACTS ### +DCAP_IMAGE_ID=0x4052beb38db7869b15596d53c2d5c02c9307faffca9215e69b0f0d0e1812a6c2 + +# On-Chain PCCS Configurations +ENCLAVE_IDENTITY_HELPER=0xfd4a34b578B352FE1896CDafaEb0f45f993352Bf +FMSPC_TCB_HELPER=0xC2A662e08A35513596E22D0aC236Ce72e59125EE +X509_CRL_HELPER=0x12C1E13Aa2a238EAb15c2e2b6AC670266bc3C814 +X509_HELPER=0x5213c0e3Ab478dbc83E8afFF8909717332E4f8E1 +ENCLAVE_ID_DAO=0x413272890ab9F155a47A5F90a404Fb51aa259087 +FMSPC_TCB_DAO=0x7c04B466DebA13D48116b1339C62b35B9805E5A0 +PCK_DAO=0x6D4cA6AE5315EBBcb4331c82531db0ad8853Eb31 +PCS_DAO=0xD0335cbC73CA2f8EDd98a2BE3909f55642F414D7 + +RISC0_VERIFIER=0x4967e2fB48E2037eC466a8b60722A94bBce48Eb7 +DCAP_ATTESTATION=0xefE368b17D137E86298eec8EbC5502fb56d27832 + +PCCS_ROUTER=0xbFDeE7A1f1bFA2267cD0DA50BE76D8c4a3864543 +V3_VERIFIER=0x67042d171b8b7da1a4a98df787bdce79190dac3c diff --git a/.github/workflows/contract-tests.yml b/.github/workflows/contract-tests.yml index ad8ffeb77..f7d50d88a 100644 --- a/.github/workflows/contract-tests.yml +++ b/.github/workflows/contract-tests.yml @@ -23,6 +23,14 @@ jobs: with: version: nightly + - name: Prepare Environment Variables + run: | + cp .env.sample.goerli .env + export $(grep -v '^#' .env | xargs) # Load variables + for var in $(grep -v '^#' .env | cut -d= -f1); do + echo "$var=${!var}" >> $GITHUB_ENV + done + - name: Setup node/yarn uses: actions/setup-node@v3 with: @@ -34,7 +42,7 @@ jobs: run: yarn - name: Build - run: forge test + run: forge test --no-match-path test/foundry/ExpressLaneBalance.t.sol tests: if: false # broken name: Contract tests @@ -53,6 +61,20 @@ jobs: with: version: nightly + - name: Prepare Environment Variables + run: | + cp .env.sample.goerli .env + export $(grep -v '^#' .env | xargs) # Load variables + for var in $(grep -v '^#' .env | cut -d= -f1); do + echo "$var=${!var}" >> $GITHUB_ENV + done + + - name: Run unused Solidity errors checker + uses: OffchainLabs/actions/check-unused-errors@main + with: + directory: './src' + exceptions_file: './test/unused-errors/exceptions.txt' + - name: Setup nodejs uses: actions/setup-node@v2 with: @@ -95,12 +117,6 @@ jobs: - name: Test function signatures run: yarn run test:signatures - - name: Run unused Solidity errors checker - uses: OffchainLabs/actions/check-unused-errors@main - with: - directory: './src' - exceptions_file: './test/unused-errors/exceptions.txt' - - name: Run coverage run: yarn hardhat coverage --testfiles "test/contract/*.spec.ts" @@ -128,7 +144,15 @@ jobs: with: version: nightly - - uses: OffchainLabs/actions/run-nitro-test-node@main + - name: Prepare Environment Variables + run: | + cp .env.sample.goerli .env + export $(grep -v '^#' .env | xargs) # Load variables + for var in $(grep -v '^#' .env | cut -d= -f1); do + echo "$var=${!var}" >> $GITHUB_ENV + done + + - uses: OffchainLabs/actions/run-nitro-test-node@test-node-args with: args: --pos no-token-bridge: true @@ -157,12 +181,20 @@ jobs: with: submodules: recursive - - uses: OffchainLabs/actions/run-nitro-test-node@main + - uses: EspressoSystems/offchainlabs-actions/run-nitro-test-node@specify-checkout-repo with: l3-node: true + args: --espresso --latest-espresso-image no-token-bridge: true no-l3-token-bridge: true nitro-contracts-branch: '${{ github.event.pull_request.head.sha || github.sha }}' + nitro-testnode-ref: celestia-integration + nitro-testnode-repo: EspressoSystems/nitro-testnode + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly - name: Setup node/yarn uses: actions/setup-node@v3 @@ -171,6 +203,14 @@ jobs: cache: 'yarn' cache-dependency-path: '**/yarn.lock' + - name: Prepare Environment Variables + run: | + cp .env.sample.goerli .env + export $(grep -v '^#' .env | xargs) # Load variables + for var in $(grep -v '^#' .env | cut -d= -f1); do + echo "$var=${!var}" >> $GITHUB_ENV + done + - name: Install packages run: yarn @@ -188,13 +228,20 @@ jobs: with: submodules: recursive - - uses: OffchainLabs/actions/run-nitro-test-node@main + - uses: EspressoSystems/offchainlabs-actions/run-nitro-test-node@specify-checkout-repo with: l3-node: true - args: --l3-fee-token + args: --l3-fee-token --espresso --latest-espresso-image no-token-bridge: true no-l3-token-bridge: true nitro-contracts-branch: '${{ github.event.pull_request.head.sha || github.sha }}' + nitro-testnode-ref: celestia-integration + nitro-testnode-repo: EspressoSystems/nitro-testnode + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly - name: Setup node/yarn uses: actions/setup-node@v3 @@ -203,6 +250,14 @@ jobs: cache: 'yarn' cache-dependency-path: '**/yarn.lock' + - name: Prepare Environment Variables + run: | + cp .env.sample.goerli .env + export $(grep -v '^#' .env | xargs) # Load variables + for var in $(grep -v '^#' .env | cut -d= -f1); do + echo "$var=${!var}" >> $GITHUB_ENV + done + - name: Install packages run: yarn @@ -220,13 +275,20 @@ jobs: with: submodules: recursive - - uses: OffchainLabs/actions/run-nitro-test-node@main + - uses: EspressoSystems/offchainlabs-actions/run-nitro-test-node@specify-checkout-repo with: l3-node: true - args: --l3-fee-token --l3-fee-token-decimals 6 + args: --espresso --latest-espresso-image --l3-fee-token --l3-fee-token-decimals 6 no-token-bridge: true no-l3-token-bridge: true nitro-contracts-branch: '${{ github.event.pull_request.head.sha || github.sha }}' + nitro-testnode-ref: 'celestia-integration' + nitro-testnode-repo: EspressoSystems/nitro-testnode + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly - name: Setup node/yarn uses: actions/setup-node@v3 @@ -235,6 +297,14 @@ jobs: cache: 'yarn' cache-dependency-path: '**/yarn.lock' + - name: Prepare Environment Variables + run: | + cp .env.sample.goerli .env + export $(grep -v '^#' .env | xargs) # Load variables + for var in $(grep -v '^#' .env | cut -d= -f1); do + echo "$var=${!var}" >> $GITHUB_ENV + done + - name: Install packages run: yarn diff --git a/.gitmodules b/.gitmodules index 888d42dcd..b8456f7da 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "lib/forge-std"] path = lib/forge-std url = https://github.com/foundry-rs/forge-std +[submodule "lib/automata-dcap-attestation"] + path = lib/automata-dcap-attestation + url = https://github.com/EspressoSystems/automata-dcap-attestation diff --git a/deploy/SequencerInboxStubCreator.js b/deploy/SequencerInboxStubCreator.js index e61a227ca..3dfc4c82e 100644 --- a/deploy/SequencerInboxStubCreator.js +++ b/deploy/SequencerInboxStubCreator.js @@ -6,6 +6,13 @@ module.exports = async hre => { const { deployer } = await getNamedAccounts() const bridge = await ethers.getContract('BridgeStub') + + const espressoTEEVerifierInboxFac = await ethers.getContractFactory( + 'EspressoTEEVerifierMock' + ) + const espressoTEEVerifier = await espressoTEEVerifierInboxFac.deploy() + await espressoTEEVerifier.deployed() + const reader4844 = await Toolkit4844.deployReader4844( await ethers.getSigner(deployer) ) @@ -24,6 +31,7 @@ module.exports = async hre => { 117964, reader4844.address, false, + espressoTEEVerifier.address, ], }) } diff --git a/foundry.toml b/foundry.toml index 33833fdee..fd3c6d021 100644 --- a/foundry.toml +++ b/foundry.toml @@ -5,9 +5,11 @@ libs = ['node_modules', 'lib'] test = 'test/foundry' cache_path = 'forge-cache/sol' optimizer = true -optimizer_runs = 100 -via_ir = false +optimizer_runs = 1 +via_ir = true +solc_version = '0.8.25' evm_version = 'cancun' +fs_permissions = [{ access = "read", path = "./"}] remappings = ['ds-test/=lib/forge-std/lib/ds-test/src/', 'forge-std/=lib/forge-std/src/', '@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/', @@ -24,4 +26,4 @@ auto_detect_remappings = false [fmt] number_underscore = 'thousands' line_length = 100 -# See more config options https://github.com/foundry-rs/foundry/tree/master/config +# See more config options https://github.com/foundry-rs/foundry/tree/master/config \ No newline at end of file diff --git a/hardhat.config.ts b/hardhat.config.ts index 262241d69..d8da2ce09 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -7,6 +7,7 @@ import 'solidity-coverage' import 'hardhat-gas-reporter' import 'hardhat-contract-sizer' import 'hardhat-ignore-warnings' +import '@nomicfoundation/hardhat-foundry' // import '@tovarishfin/hardhat-yul'; import dotenv from 'dotenv' @@ -15,18 +16,19 @@ dotenv.config() const solidity = { compilers: [ { - version: '0.8.9', + version: '0.8.25', settings: { optimizer: { enabled: true, runs: 100, }, + viaIR: true, }, }, ], overrides: { 'src/rollup/RollupUserLogic.sol': { - version: '0.8.26', + version: '0.8.20', settings: { optimizer: { enabled: true, @@ -221,6 +223,14 @@ module.exports = { browserURL: 'https://sepolia.arbiscan.io/', }, }, + { + network: 'baseSepolia', + chainId: 84532, + urls: { + apiURL: 'https://api-sepolia.basescan.org/api', + browserURL: 'https://sepolia.basescan.org/', + }, + }, ], }, mocha: { diff --git a/lib/automata-dcap-attestation b/lib/automata-dcap-attestation new file mode 160000 index 000000000..913c0f0e9 --- /dev/null +++ b/lib/automata-dcap-attestation @@ -0,0 +1 @@ +Subproject commit 913c0f0e97d0976b9c317b591ed83ac319d05c3a diff --git a/lib/forge-std b/lib/forge-std index e8a047e3f..83c5d212a 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit e8a047e3f40f13fa37af6fe14e6e06283d9a060e +Subproject commit 83c5d212a01f8950727da4095cdfe2654baccb5b diff --git a/package.json b/package.json index 6de0e726b..c7260b3c1 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "prepublishOnly": "hardhat clean && forge clean && hardhat compile && yarn build:forge:yul", "build:all": "yarn build && yarn build:forge", "build": "hardhat compile", - "build:forge:sol": "forge build --skip *.yul", + "build:forge:sol": "forge build --skip *.yul test/foundry/ExpressLaneBalance.t.sol", "build:forge:yul": "FOUNDRY_PROFILE=yul forge build --skip *.sol", "build:forge": "yarn build:forge:sol && yarn build:forge:yul", "contract:size": "hardhat size-contracts", @@ -50,9 +50,10 @@ "deploy-cachemanager-testnode": "hardhat run scripts/local-deployment/deployCacheManager.ts" }, "dependencies": { + "@nomicfoundation/hardhat-verify": "^2.0.12", "@offchainlabs/upgrade-executor": "1.1.0-beta.0", - "@openzeppelin/contracts": "4.5.0", - "@openzeppelin/contracts-upgradeable": "4.5.2", + "@openzeppelin/contracts": "4.8.0", + "@openzeppelin/contracts-upgradeable": "4.8.0", "patch-package": "^6.4.7", "solady": "0.0.182" }, @@ -60,7 +61,7 @@ "devDependencies": { "@arbitrum/sdk": "^3.4.1", "@ethersproject/providers": "^5.7.2", - "@nomicfoundation/hardhat-verify": "^2.0.9", + "@nomicfoundation/hardhat-foundry": "^1.1.2", "@nomiclabs/hardhat-ethers": "npm:hardhat-deploy-ethers@^0.3.0-beta.13", "@nomiclabs/hardhat-waffle": "^2.0.1", "@tovarishfin/hardhat-yul": "^3.0.5", diff --git a/remappings.txt b/remappings.txt new file mode 100644 index 000000000..c59465597 --- /dev/null +++ b/remappings.txt @@ -0,0 +1,8 @@ +ds-test/=lib/forge-std/lib/ds-test/src/ +forge-std/=lib/forge-std/src/ +openzeppelin-contracts/=node_modules/@openzeppelin/contracts/ +@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/ +@openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/ +@automata-network/dcap-attestation/contracts=lib/automata-dcap-attestation/contracts/ +@automata-network/dcap-attestation/test=lib/automata-dcap-attestation/forge-test/ +solady/=node_modules/solady/src/ \ No newline at end of file diff --git a/scripts/config.ts.example b/scripts/config.ts.example index 95df68514..25c9d2fd5 100644 --- a/scripts/config.ts.example +++ b/scripts/config.ts.example @@ -24,6 +24,7 @@ export const config = { delaySeconds: ethers.BigNumber.from('86400'), futureSeconds: ethers.BigNumber.from('3600'), }, + espressoTEEVerifier: '0xb562622f2D76F355D673560CB88c1dF6088702f1', }, validators: [ '0x1234123412341234123412341234123412341234', diff --git a/scripts/createERC20Rollup.ts b/scripts/createERC20Rollup.ts index 49947b270..c2c76149e 100644 --- a/scripts/createERC20Rollup.ts +++ b/scripts/createERC20Rollup.ts @@ -34,11 +34,17 @@ async function main() { throw new Error('ROLLUP_CREATOR_ADDRESS not set') } + const espressoTEEVerifierAddress = process.env.ESPRESSO_TEE_VERIFIER_ADDRESS + if (!espressoTEEVerifierAddress) { + throw new Error('ESPRESSO_TEE_VERIFIER_ADDRESS not set') + } + console.log('Creating new rollup with', customFeeTokenAddress, 'as fee token') await createRollup( deployer, false, rollupCreatorAddress, + espressoTEEVerifierAddress, customFeeTokenAddress ) } diff --git a/scripts/createEthRollup.ts b/scripts/createEthRollup.ts index c76af5dd3..d0d4f404e 100644 --- a/scripts/createEthRollup.ts +++ b/scripts/createEthRollup.ts @@ -5,13 +5,23 @@ import { createRollup } from './rollupCreation' async function main() { const feeToken = ethers.constants.AddressZero const rollupCreatorAddress = process.env.ROLLUP_CREATOR_ADDRESS + const espressoTEEVerifierAddress = process.env.ESPRESSO_TEE_VERIFIER_ADDRESS if (!rollupCreatorAddress) { throw new Error('ROLLUP_CREATOR_ADDRESS not set') } + if (!espressoTEEVerifierAddress) { + throw new Error('ESPRESSO_TEE_VERIFIER_ADDRESS not set') + } const [signer] = await ethers.getSigners() - await createRollup(signer, false, rollupCreatorAddress, feeToken) + await createRollup( + signer, + false, + rollupCreatorAddress, + espressoTEEVerifierAddress, + feeToken + ) } main() diff --git a/scripts/deployEspressoTEEVerifier.ts b/scripts/deployEspressoTEEVerifier.ts new file mode 100644 index 000000000..e87984082 --- /dev/null +++ b/scripts/deployEspressoTEEVerifier.ts @@ -0,0 +1,40 @@ +import '@nomiclabs/hardhat-ethers' +import { ethers } from 'hardhat' +import { deployContract } from './deploymentUtils' + +async function main() { + const [deployer] = await ethers.getSigners() + + const v3QuoteVerifier = process.env.V3_QUOTE_VERIFIER_ADDRESS + if (!v3QuoteVerifier) { + throw new Error('V3_QUOTE_VERIFIER_ADDRESS not set') + } + + const mrEnclave = process.env.MR_ENCLAVE + if (!mrEnclave) { + throw new Error('MR_ENCLAVE not set') + } + + const mrSigner = process.env.MR_SIGNER + if (!mrSigner) { + throw new Error('MR_SIGNER not set') + } + + const esperssoTEEVerifier = await deployContract( + 'EspressoTEEVerifier', + deployer, + [mrEnclave, mrSigner, v3QuoteVerifier], + true + ) + console.log( + 'EspressoTEEVerifier deployed at address:', + esperssoTEEVerifier.address + ) +} + +main() + .then(() => process.exit(0)) + .catch((error: Error) => { + console.error(error) + process.exit(1) + }) diff --git a/scripts/deploymentUtils.ts b/scripts/deploymentUtils.ts index 168fd9d67..ae7855de9 100644 --- a/scripts/deploymentUtils.ts +++ b/scripts/deploymentUtils.ts @@ -205,6 +205,7 @@ export async function deployAllContracts( hostIoArg, verify ) + const osp: Contract = await deployContract( 'OneStepProofEntry', signer, diff --git a/scripts/local-deployment/deployCreatorAndCreateRollup.ts b/scripts/local-deployment/deployCreatorAndCreateRollup.ts index aca456173..fdd251fe4 100644 --- a/scripts/local-deployment/deployCreatorAndCreateRollup.ts +++ b/scripts/local-deployment/deployCreatorAndCreateRollup.ts @@ -1,6 +1,6 @@ import { ethers } from 'hardhat' import '@nomiclabs/hardhat-ethers' -import { deployAllContracts } from '../deploymentUtils' +import { deployAllContracts, deployContract } from '../deploymentUtils' import { createRollup } from '../rollupCreation' import { promises as fs } from 'fs' import { BigNumber } from 'ethers' @@ -51,6 +51,14 @@ async function main() { console.log('Deploy RollupCreator') const contracts = await deployAllContracts(deployerWallet, maxDataSize, false, espressoLightClientAddr) + // for local deployment, we use a mock address + const espressoTEEVerifierMock = await deployContract( + 'EspressoTEEVerifierMock', + deployerWallet, + [], + false + ) + console.log('Set templates on the Rollup Creator') await ( await contracts.rollupCreator.setTemplates( @@ -75,10 +83,12 @@ async function main() { 'using RollupCreator', contracts.rollupCreator.address ) + const result = await createRollup( deployerWallet, true, contracts.rollupCreator.address, + espressoTEEVerifierMock.address, feeToken ) diff --git a/scripts/rollupCreation.ts b/scripts/rollupCreation.ts index ce16290f1..cb0ac61bd 100644 --- a/scripts/rollupCreation.ts +++ b/scripts/rollupCreation.ts @@ -61,6 +61,7 @@ export async function createRollup( signer: Signer, isDevDeployment: boolean, rollupCreatorAddress: string, + espressoTEEVerifierAddress: string, feeToken: string ): Promise<{ rollupCreationResult: RollupCreationResult @@ -100,8 +101,13 @@ export async function createRollup( // Call the createRollup function console.log('Calling createRollup to generate a new rollup ...') + const deployParams = isDevDeployment - ? await _getDevRollupConfig(feeToken, validatorWalletCreator) + ? await _getDevRollupConfig( + feeToken, + validatorWalletCreator, + espressoTEEVerifierAddress + ) : { config: config.rollupConfig, validators: config.validators, @@ -222,7 +228,8 @@ export async function createRollup( async function _getDevRollupConfig( feeToken: string, - validatorWalletCreator: string + validatorWalletCreator: string, + espressoTEEVerifierAddress: string ) { // set up owner address const ownerAddress = @@ -313,6 +320,7 @@ async function _getDevRollupConfig( delaySeconds: ethers.BigNumber.from('86400'), futureSeconds: ethers.BigNumber.from('3600'), }, + espressoTEEVerifier: espressoTEEVerifierAddress, }, validators: validators, maxDataSize: _maxDataSize, diff --git a/src/bridge/EspressoTEEVerifier.sol b/src/bridge/EspressoTEEVerifier.sol new file mode 100644 index 000000000..a2a8eb077 --- /dev/null +++ b/src/bridge/EspressoTEEVerifier.sol @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { + V3QuoteVerifier +} from "@automata-network/dcap-attestation/contracts/verifiers/V3QuoteVerifier.sol"; +import {BELE} from "@automata-network/dcap-attestation/contracts/utils/BELE.sol"; +import {Header} from "@automata-network/dcap-attestation/contracts/types/CommonStruct.sol"; +import { + IQuoteVerifier +} from "@automata-network/dcap-attestation/contracts/interfaces/IQuoteVerifier.sol"; +import { + HEADER_LENGTH, + ENCLAVE_REPORT_LENGTH +} from "@automata-network/dcap-attestation/contracts/types/Constants.sol"; +import {EnclaveReport} from "@automata-network/dcap-attestation/contracts/types/V3Structs.sol"; +import { + V3QuoteVerifier +} from "@automata-network/dcap-attestation/contracts/verifiers/V3QuoteVerifier.sol"; +import {BytesUtils} from "@automata-network/dcap-attestation/contracts/utils/BytesUtils.sol"; +import {Ownable} from "solady/auth/Ownable.sol"; +import {IEspressoTEEVerifier} from "./IEspressoTEEVerifier.sol"; + +/** + * + * @title Verifies quotes from the TEE and attests on-chain + * @notice Contains the logic to verify a quote from the TEE and attest on-chain. It uses the V3QuoteVerifier contract + * from automata to verify the quote. Along with some additional verification logic. + */ +contract EspressoTEEVerifier is IEspressoTEEVerifier, Ownable { + using BytesUtils for bytes; + + // V3QuoteVerififer contract from automata to verify the quote + V3QuoteVerifier public quoteVerifier; + bytes32 public mrEnclave; + bytes32 public mrSigner; + + constructor(bytes32 _mrEnclave, bytes32 _mrSigner, address _quoteVerifier) { + quoteVerifier = V3QuoteVerifier(_quoteVerifier); + mrEnclave = _mrEnclave; + mrSigner = _mrSigner; + _initializeOwner(msg.sender); + } + + /* + @notice Verify a quote from the TEE and attest on-chain + The verification is considered successful if the function does not revert. + @param rawQuote The quote from the TEE + @param reportDataHash The hash of the report data + */ + function verify(bytes calldata rawQuote, bytes32 reportDataHash) external view { + // Parse the header + Header memory header = parseQuoteHeader(rawQuote); + + // Currently only version 3 is supported + if (header.version != 3) { + revert InvalidHeaderVersion(); + } + + // Verify the quote + (bool success, ) = quoteVerifier.verifyQuote(header, rawQuote); + if (!success) { + revert InvalidQuote(); + } + + // // Parse enclave quote + uint256 offset = HEADER_LENGTH + ENCLAVE_REPORT_LENGTH; + EnclaveReport memory localReport; + (success, localReport) = parseEnclaveReport(rawQuote[HEADER_LENGTH:offset]); + if (!success) { + revert FailedToParseEnclaveReport(); + } + + // Check that mrEnclave and mrSigner match + if (localReport.mrEnclave != mrEnclave || localReport.mrSigner != mrSigner) { + revert InvalidMREnclaveOrSigner(); + } + + // Verify that the reportDataHash if the hash signed by the TEE + // We do not check the signature because `quoteVerifier.verifyQuote` already does that + if (reportDataHash != bytes32(localReport.reportData.substring(0, 32))) { + revert InvalidReportDataHash(); + } + } + + /* + @notice Parses the header from the quote + @param rawQuote The raw quote in bytes + @return header The parsed header + */ + function parseQuoteHeader(bytes calldata rawQuote) public pure returns (Header memory header) { + bytes2 attestationKeyType = bytes2(rawQuote[2:4]); + bytes2 qeSvn = bytes2(rawQuote[8:10]); + bytes2 pceSvn = bytes2(rawQuote[10:12]); + bytes16 qeVendorId = bytes16(rawQuote[12:28]); + + header = Header({ + version: uint16(BELE.leBytesToBeUint(rawQuote[0:2])), + attestationKeyType: attestationKeyType, + teeType: bytes4(uint32(BELE.leBytesToBeUint(rawQuote[4:8]))), + qeSvn: qeSvn, + pceSvn: pceSvn, + qeVendorId: qeVendorId, + userData: bytes20(rawQuote[28:48]) + }); + } + + /* + @notice Parses the enclave report from the quote + @param rawEnclaveReport The raw enclave report from the quote in bytes + @return success True if the enclave report was parsed successfully + @return enclaveReport The parsed enclave report + */ + function parseEnclaveReport( + bytes memory rawEnclaveReport + ) public pure returns (bool success, EnclaveReport memory enclaveReport) { + if (rawEnclaveReport.length != ENCLAVE_REPORT_LENGTH) { + return (false, enclaveReport); + } + enclaveReport.cpuSvn = bytes16(rawEnclaveReport.substring(0, 16)); + enclaveReport.miscSelect = bytes4(rawEnclaveReport.substring(16, 4)); + enclaveReport.reserved1 = bytes28(rawEnclaveReport.substring(20, 28)); + enclaveReport.attributes = bytes16(rawEnclaveReport.substring(48, 16)); + enclaveReport.mrEnclave = bytes32(rawEnclaveReport.substring(64, 32)); + enclaveReport.reserved2 = bytes32(rawEnclaveReport.substring(96, 32)); + enclaveReport.mrSigner = bytes32(rawEnclaveReport.substring(128, 32)); + enclaveReport.reserved3 = rawEnclaveReport.substring(160, 96); + enclaveReport.isvProdId = uint16(BELE.leBytesToBeUint(rawEnclaveReport.substring(256, 2))); + enclaveReport.isvSvn = uint16(BELE.leBytesToBeUint(rawEnclaveReport.substring(258, 2))); + enclaveReport.reserved4 = rawEnclaveReport.substring(260, 60); + enclaveReport.reportData = rawEnclaveReport.substring(320, 64); + success = true; + } + + /* + * @dev Set the mrEnclave of the contract + */ + function setMrEnclave(bytes32 _mrEnclave) external onlyOwner { + mrEnclave = _mrEnclave; + } + + /* + * @dev Set the mrSigner of the contract + */ + function setMrSigner(bytes32 _mrSigner) external onlyOwner { + mrSigner = _mrSigner; + } +} diff --git a/src/bridge/IEspressoTEEVerifier.sol b/src/bridge/IEspressoTEEVerifier.sol new file mode 100644 index 000000000..5367455f5 --- /dev/null +++ b/src/bridge/IEspressoTEEVerifier.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {Header} from "@automata-network/dcap-attestation/contracts/types/CommonStruct.sol"; +import {EnclaveReport} from "@automata-network/dcap-attestation/contracts/types/V3Structs.sol"; + +interface IEspressoTEEVerifier { + // We only support version 3 for now + error InvalidHeaderVersion(); + // This error is thrown when the automata verification fails + error InvalidQuote(); + // This error is thrown when the enclave report fails to parse + error FailedToParseEnclaveReport(); + // This error is thrown when the mrEnclave and mrSigner don't match + error InvalidMREnclaveOrSigner(); + // This error is thrown when the reportDataHash doesn't match the hash signed by the TEE + error InvalidReportDataHash(); + + function verify(bytes calldata rawQuote, bytes32 reportDataHash) external view; + + function parseQuoteHeader(bytes calldata rawQuote) external pure returns (Header memory header); + + function parseEnclaveReport( + bytes memory rawEnclaveReport + ) external pure returns (bool success, EnclaveReport memory enclaveReport); + + function setMrEnclave(bytes32 _mrEnclave) external; + + function setMrSigner(bytes32 _mrSigner) external; +} diff --git a/src/bridge/ISequencerInbox.sol b/src/bridge/ISequencerInbox.sol index 47db30f00..eba36ad60 100644 --- a/src/bridge/ISequencerInbox.sol +++ b/src/bridge/ISequencerInbox.sol @@ -39,6 +39,9 @@ interface ISequencerInbox is IDelayedMessageProvider { /// @dev a keyset was invalidated event InvalidateKeyset(bytes32 indexed keysetHash); + /// @dev a TEE attestation quote was verified + event TEEAttestationQuoteVerified(uint256 indexed seqMessageIndex); + function totalDelayedMessagesRead() external view returns (uint256); function bridge() external view returns (IBridge); @@ -162,6 +165,16 @@ interface ISequencerInbox is IDelayedMessageProvider { uint256 newMessageCount ) external; + function addSequencerL2BatchFromOrigin( + uint256 sequenceNumber, + bytes calldata data, + uint256 afterDelayedMessagesRead, + IGasRefunder gasRefunder, + uint256 prevMessageCount, + uint256 newMessageCount, + bytes memory quote + ) external; + function addSequencerL2Batch( uint256 sequenceNumber, bytes calldata data, @@ -171,6 +184,16 @@ interface ISequencerInbox is IDelayedMessageProvider { uint256 newMessageCount ) external; + function addSequencerL2Batch( + uint256 sequenceNumber, + bytes calldata data, + uint256 afterDelayedMessagesRead, + IGasRefunder gasRefunder, + uint256 prevMessageCount, + uint256 newMessageCount, + bytes memory quote + ) external; + function addSequencerL2BatchFromBlobs( uint256 sequenceNumber, uint256 afterDelayedMessagesRead, @@ -226,4 +249,10 @@ interface ISequencerInbox is IDelayedMessageProvider { // ---------- initializer ---------- function initialize(IBridge bridge_, MaxTimeVariation calldata maxTimeVariation_) external; + + function initialize( + IBridge bridge_, + MaxTimeVariation calldata maxTimeVariation_, + address _espressoTEEVerifier + ) external; } diff --git a/src/bridge/SequencerInbox.sol b/src/bridge/SequencerInbox.sol index 16a65e770..7cff95e2a 100644 --- a/src/bridge/SequencerInbox.sol +++ b/src/bridge/SequencerInbox.sol @@ -46,6 +46,7 @@ import {IGasRefunder} from "../libraries/IGasRefunder.sol"; import {GasRefundEnabled} from "../libraries/GasRefundEnabled.sol"; import "../libraries/ArbitrumChecker.sol"; import {IERC20Bridge} from "./IERC20Bridge.sol"; +import {IEspressoTEEVerifier} from "../bridge/IEspressoTEEVerifier.sol"; /** * @title Accepts batches from the sequencer and adds them to the rollup inbox. @@ -125,11 +126,9 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox // True if the chain this SequencerInbox is deployed on uses custom fee token bool public immutable isUsingFeeToken; - constructor( - uint256 _maxDataSize, - IReader4844 reader4844_, - bool _isUsingFeeToken - ) { + IEspressoTEEVerifier public espressoTEEVerifier; + + constructor(uint256 _maxDataSize, IReader4844 reader4844_, bool _isUsingFeeToken) { maxDataSize = _maxDataSize; if (hostChainIsArbitrum) { if (reader4844_ != IReader4844(address(0))) revert DataBlobsNotSupported(); @@ -176,9 +175,18 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox __LEGACY_MAX_TIME_VARIATION.futureSeconds = 0; } + /** + Deprecated because we created another `initialize` function that accepts the `EspressoTEEVerifier` contract + address as a parameter which is used by the `SequencerInbox` contract to verify the TEE attestation quote. + */ + function initialize(IBridge, ISequencerInbox.MaxTimeVariation calldata) external onlyDelegated { + revert Deprecated(); + } + function initialize( IBridge bridge_, - ISequencerInbox.MaxTimeVariation calldata maxTimeVariation_ + ISequencerInbox.MaxTimeVariation calldata maxTimeVariation_, + address _espressoTEEVerifier ) external onlyDelegated { if (bridge != IBridge(address(0))) revert AlreadyInit(); if (bridge_ == IBridge(address(0))) revert HadZeroInit(); @@ -199,6 +207,7 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox rollup = bridge_.rollup(); _setMaxTimeVariation(maxTimeVariation_); + espressoTEEVerifier = IEspressoTEEVerifier(_espressoTEEVerifier); } /// @notice Allows the rollup owner to sync the rollup address @@ -238,16 +247,7 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox futureSeconds = 1; } - function maxTimeVariation() - external - view - returns ( - uint256, - uint256, - uint256, - uint256 - ) - { + function maxTimeVariation() external view returns (uint256, uint256, uint256, uint256) { ( uint64 delayBlocks_, uint64 futureBlocks_, @@ -263,16 +263,7 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox ); } - function maxTimeVariationInternal() - internal - view - returns ( - uint64, - uint64, - uint64, - uint64 - ) - { + function maxTimeVariationInternal() internal view returns (uint64, uint64, uint64, uint64) { if (_chainIdChanged()) { return (1, 1, 1, 1); } else { @@ -352,17 +343,49 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox revert Deprecated(); } + /** + Deprecated because we added a new method with TEE attestation quote + to verify that the batch is posted by the batch poster running in TEE. + */ + function addSequencerL2BatchFromOrigin( + uint256, + bytes calldata, + uint256, + IGasRefunder gasRefunder, + uint256, + uint256 + ) external refundsGas(gasRefunder, IReader4844(address(0))) { + revert Deprecated(); + } + function addSequencerL2BatchFromOrigin( uint256 sequenceNumber, bytes calldata data, uint256 afterDelayedMessagesRead, IGasRefunder gasRefunder, uint256 prevMessageCount, - uint256 newMessageCount + uint256 newMessageCount, + bytes memory quote ) external refundsGas(gasRefunder, IReader4844(address(0))) { // solhint-disable-next-line avoid-tx-origin if (msg.sender != tx.origin) revert NotOrigin(); if (!isBatchPoster[msg.sender]) revert NotBatchPoster(); + + // take keccak2256 hash of all the function arguments except the quote + bytes32 reportDataHash = keccak256( + abi.encode( + sequenceNumber, + data, + afterDelayedMessagesRead, + address(gasRefunder), + prevMessageCount, + newMessageCount + ) + ); + // verify the quote for the batch poster running in the TEE + espressoTEEVerifier.verify(quote, reportDataHash); + emit TEEAttestationQuoteVerified(sequenceNumber); + (bytes32 dataHash, IBridge.TimeBounds memory timeBounds) = formCallDataHash( data, afterDelayedMessagesRead @@ -465,15 +488,61 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox } } + /** + Deprecated because we added a new method with TEE attestation quote + to verify that the batch is posted by the batch poster running in TEE. + */ + function addSequencerL2Batch( + uint256, + bytes calldata, + uint256, + IGasRefunder gasRefunder, + uint256, + uint256 + ) external override refundsGas(gasRefunder, IReader4844(address(0))) { + revert Deprecated(); + } + + /* + * addSequencerL2Batch is called by either the rollup admin or batch poster + * running in TEE to add a new batch + * @param sequenceNumber - the sequence number of the batch + * @param data - the data of the batch + * @param afterDelayedMessagesRead - the number of delayed messages read by the sequencer + * @param gasRefunder - the gas refunder contract + * @param prevMessageCount - the number of messages in the previous batch + * @param newMessageCount - the number of messages in the new batch + * @param quote - the atttestation quote from the TEE + */ function addSequencerL2Batch( uint256 sequenceNumber, bytes calldata data, uint256 afterDelayedMessagesRead, IGasRefunder gasRefunder, uint256 prevMessageCount, - uint256 newMessageCount + uint256 newMessageCount, + bytes memory quote ) external override refundsGas(gasRefunder, IReader4844(address(0))) { if (!isBatchPoster[msg.sender] && msg.sender != address(rollup)) revert NotBatchPoster(); + + // Only check the attestation quote if the batch has been posted by the + // batch poster + if (isBatchPoster[msg.sender]) { + // take keccak2256 hash of all the function arguments except the quote + bytes32 reportDataHash = keccak256( + abi.encode( + sequenceNumber, + data, + afterDelayedMessagesRead, + address(gasRefunder), + prevMessageCount, + newMessageCount + ) + ); + // verify the quote for the batch poster running in the TEE + espressoTEEVerifier.verify(quote, reportDataHash); + emit TEEAttestationQuoteVerified(sequenceNumber); + } (bytes32 dataHash, IBridge.TimeBounds memory timeBounds) = formCallDataHash( data, afterDelayedMessagesRead @@ -518,11 +587,9 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox emit SequencerBatchData(seqMessageIndex, data); } - function packHeader(uint256 afterDelayedMessagesRead) - internal - view - returns (bytes memory, IBridge.TimeBounds memory) - { + function packHeader( + uint256 afterDelayedMessagesRead + ) internal view returns (bytes memory, IBridge.TimeBounds memory) { IBridge.TimeBounds memory timeBounds = getTimeBounds(); bytes memory header = abi.encodePacked( timeBounds.minTimestamp, @@ -540,11 +607,9 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox /// @param afterDelayedMessagesRead The delayed messages count read up to /// @return The data hash /// @return The timebounds within which the message should be processed - function formEmptyDataHash(uint256 afterDelayedMessagesRead) - internal - view - returns (bytes32, IBridge.TimeBounds memory) - { + function formEmptyDataHash( + uint256 afterDelayedMessagesRead + ) internal view returns (bytes32, IBridge.TimeBounds memory) { (bytes memory header, IBridge.TimeBounds memory timeBounds) = packHeader( afterDelayedMessagesRead ); @@ -569,11 +634,10 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox /// @param afterDelayedMessagesRead The delayed messages count read up to /// @return The data hash /// @return The timebounds within which the message should be processed - function formCallDataHash(bytes calldata data, uint256 afterDelayedMessagesRead) - internal - view - returns (bytes32, IBridge.TimeBounds memory) - { + function formCallDataHash( + bytes calldata data, + uint256 afterDelayedMessagesRead + ) internal view returns (bytes32, IBridge.TimeBounds memory) { uint256 fullDataLen = HEADER_LENGTH + data.length; if (fullDataLen > maxDataSize) revert DataTooLarge(fullDataLen, maxDataSize); @@ -606,15 +670,9 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox /// @return The data hash /// @return The timebounds within which the message should be processed /// @return The normalized amount of gas used for blob posting - function formBlobDataHash(uint256 afterDelayedMessagesRead) - internal - view - returns ( - bytes32, - IBridge.TimeBounds memory, - uint256 - ) - { + function formBlobDataHash( + uint256 afterDelayedMessagesRead + ) internal view returns (bytes32, IBridge.TimeBounds memory, uint256) { bytes32[] memory dataHashes = reader4844.getDataHashes(); if (dataHashes.length == 0) revert MissingDataHashes(); @@ -679,12 +737,7 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox uint256 newMessageCount ) internal - returns ( - uint256 seqMessageIndex, - bytes32 beforeAcc, - bytes32 delayedAcc, - bytes32 acc - ) + returns (uint256 seqMessageIndex, bytes32 beforeAcc, bytes32 delayedAcc, bytes32 acc) { if (afterDelayedMessagesRead < totalDelayedMessagesRead) revert DelayedBackwards(); if (afterDelayedMessagesRead > bridge.delayedMessageCount()) revert DelayedTooFar(); @@ -712,9 +765,9 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox return bridge.sequencerMessageCount(); } - function _setMaxTimeVariation(ISequencerInbox.MaxTimeVariation memory maxTimeVariation_) - internal - { + function _setMaxTimeVariation( + ISequencerInbox.MaxTimeVariation memory maxTimeVariation_ + ) internal { if ( maxTimeVariation_.delayBlocks > type(uint64).max || maxTimeVariation_.futureBlocks > type(uint64).max || @@ -730,19 +783,18 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox } /// @inheritdoc ISequencerInbox - function setMaxTimeVariation(ISequencerInbox.MaxTimeVariation memory maxTimeVariation_) - external - onlyRollupOwner - { + function setMaxTimeVariation( + ISequencerInbox.MaxTimeVariation memory maxTimeVariation_ + ) external onlyRollupOwner { _setMaxTimeVariation(maxTimeVariation_); emit OwnerFunctionCalled(0); } /// @inheritdoc ISequencerInbox - function setIsBatchPoster(address addr, bool isBatchPoster_) - external - onlyRollupOwnerOrBatchPosterManager - { + function setIsBatchPoster( + address addr, + bool isBatchPoster_ + ) external onlyRollupOwnerOrBatchPosterManager { isBatchPoster[addr] = isBatchPoster_; emit OwnerFunctionCalled(1); } @@ -778,10 +830,10 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox } /// @inheritdoc ISequencerInbox - function setIsSequencer(address addr, bool isSequencer_) - external - onlyRollupOwnerOrBatchPosterManager - { + function setIsSequencer( + address addr, + bool isSequencer_ + ) external onlyRollupOwnerOrBatchPosterManager { isSequencer[addr] = isSequencer_; emit OwnerFunctionCalled(4); // Owner in this context can also be batch poster manager } @@ -792,6 +844,11 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox emit OwnerFunctionCalled(5); } + function setEspressoTEEVerifier(address _espressoTEEVerifier) external onlyRollupOwner { + espressoTEEVerifier = IEspressoTEEVerifier(_espressoTEEVerifier); + emit OwnerFunctionCalled(6); + } + function isValidKeysetHash(bytes32 ksHash) external view returns (bool) { return dasKeySetInfo[ksHash].isValidKeyset; } diff --git a/src/chain/CacheManager.sol b/src/chain/CacheManager.sol index 76fca637e..ba8a1d791 100644 --- a/src/chain/CacheManager.sol +++ b/src/chain/CacheManager.sol @@ -8,7 +8,7 @@ import "../precompiles/ArbOwnerPublic.sol"; import "../precompiles/ArbWasm.sol"; import "../precompiles/ArbWasmCache.sol"; import "../libraries/DelegateCallAware.sol"; -import "solady/src/utils/MinHeapLib.sol"; +import "solady/utils/MinHeapLib.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; contract CacheManager is Initializable, DelegateCallAware { diff --git a/src/mocks/EspressoTEEVerifier.sol b/src/mocks/EspressoTEEVerifier.sol new file mode 100644 index 000000000..3b0518c0b --- /dev/null +++ b/src/mocks/EspressoTEEVerifier.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/** + * + * @title Verifies quotes from the TEE and attests on-chain + * @notice Contains the logic to verify a quote from the TEE and attest on-chain. It uses the V3QuoteVerifier contract + * to verify the quote. Along with some additional verification logic. + */ + +contract EspressoTEEVerifierMock { + constructor() {} + + function verify( + bytes calldata rawQuote, + bytes32 reportDataHash + ) external view returns (bool success) { + return (true); + } +} diff --git a/src/mocks/SequencerInboxStub.sol b/src/mocks/SequencerInboxStub.sol index 49b317020..9b098c69f 100644 --- a/src/mocks/SequencerInboxStub.sol +++ b/src/mocks/SequencerInboxStub.sol @@ -15,7 +15,8 @@ contract SequencerInboxStub is SequencerInbox { ISequencerInbox.MaxTimeVariation memory maxTimeVariation_, uint256 maxDataSize_, IReader4844 reader4844_, - bool isUsingFeeToken_ + bool isUsingFeeToken_, + address espressoTEEVerifier_ ) SequencerInbox(maxDataSize_, reader4844_, isUsingFeeToken_) { bridge = bridge_; rollup = IOwnable(msg.sender); @@ -24,6 +25,7 @@ contract SequencerInboxStub is SequencerInbox { delaySeconds = uint64(maxTimeVariation_.delaySeconds); futureSeconds = uint64(maxTimeVariation_.futureSeconds); isBatchPoster[sequencer_] = true; + espressoTEEVerifier = IEspressoTEEVerifier(espressoTEEVerifier_); } function addInitMessage(uint256 chainId) external { diff --git a/src/mocks/Simple.sol b/src/mocks/Simple.sol index b9aafc74c..e0c63733d 100644 --- a/src/mocks/Simple.sol +++ b/src/mocks/Simple.sol @@ -150,7 +150,8 @@ contract Simple { delayedMessagesRead, IGasRefunder(address(0)), 0, - 0 + 0, + "" ); sequenceNumber++; } diff --git a/src/rollup/BridgeCreator.sol b/src/rollup/BridgeCreator.sol index 0e45f8152..3228f1992 100644 --- a/src/rollup/BridgeCreator.sol +++ b/src/rollup/BridgeCreator.sol @@ -51,10 +51,10 @@ contract BridgeCreator is Ownable { emit ERC20TemplatesUpdated(); } - function _createBridge(address adminProxy, BridgeContracts storage templates) - internal - returns (BridgeContracts memory) - { + function _createBridge( + address adminProxy, + BridgeContracts storage templates + ) internal returns (BridgeContracts memory) { BridgeContracts memory frame; frame.bridge = IBridge( address(new TransparentUpgradeableProxy(address(templates.bridge), adminProxy, "")) @@ -78,11 +78,28 @@ contract BridgeCreator is Ownable { return frame; } + /* + * Deprecated because we added a new method to create bridges + * which requires the address of the `EspressoTEEVerifier` contract + * to be passed in. `EspressoTEEVerifier` is required by the sequencer + * inbox contract to verify the quote from the TEE to check if the batch has + * been posted by a batch poster running in the TEE. + */ + function createBridge( + address, + address, + address, + ISequencerInbox.MaxTimeVariation calldata + ) external returns (BridgeContracts memory) { + revert Deprecated(); + } + function createBridge( address adminProxy, address rollup, address nativeToken, - ISequencerInbox.MaxTimeVariation calldata maxTimeVariation + ISequencerInbox.MaxTimeVariation calldata maxTimeVariation, + address espressoTEEVerifier ) external returns (BridgeContracts memory) { // create ETH-based bridge if address zero is provided for native token, otherwise create ERC20-based bridge BridgeContracts memory frame = _createBridge( @@ -96,7 +113,11 @@ contract BridgeCreator is Ownable { } else { IERC20Bridge(address(frame.bridge)).initialize(IOwnable(rollup), nativeToken); } - frame.sequencerInbox.initialize(IBridge(frame.bridge), maxTimeVariation); + frame.sequencerInbox.initialize( + IBridge(frame.bridge), + maxTimeVariation, + espressoTEEVerifier + ); frame.inbox.initialize(frame.bridge, frame.sequencerInbox); frame.rollupEventInbox.initialize(frame.bridge); frame.outbox.initialize(frame.bridge); diff --git a/src/rollup/Config.sol b/src/rollup/Config.sol index 13ca82e2a..750d80e66 100644 --- a/src/rollup/Config.sol +++ b/src/rollup/Config.sol @@ -26,6 +26,8 @@ struct Config { string chainConfig; uint64 genesisBlockNum; ISequencerInbox.MaxTimeVariation sequencerInboxMaxTimeVariation; + // address of the TEE verifier contract + address espressoTEEVerifier; } struct ContractDependencies { diff --git a/src/rollup/RollupAdminLogic.sol b/src/rollup/RollupAdminLogic.sol index 0579eaaf2..b348b2df8 100644 --- a/src/rollup/RollupAdminLogic.sol +++ b/src/rollup/RollupAdminLogic.sol @@ -16,12 +16,10 @@ import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; import {NO_CHAL_INDEX} from "../libraries/Constants.sol"; contract RollupAdminLogic is RollupCore, IRollupAdmin, DoubleLogicUUPSUpgradeable { - function initialize(Config calldata config, ContractDependencies calldata connectedContracts) - external - override - onlyProxy - initializer - { + function initialize( + Config calldata config, + ContractDependencies calldata connectedContracts + ) external override onlyProxy initializer { rollupDeploymentBlock = block.number; bridge = connectedContracts.bridge; sequencerInbox = connectedContracts.sequencerInbox; @@ -44,7 +42,8 @@ contract RollupAdminLogic is RollupCore, IRollupAdmin, DoubleLogicUUPSUpgradeabl 1, IGasRefunder(address(0)), 0, - 1 + 1, + "" ); validatorUtils = connectedContracts.validatorUtils; @@ -255,11 +254,10 @@ contract RollupAdminLogic is RollupCore, IRollupAdmin, DoubleLogicUUPSUpgradeabl emit OwnerFunctionCalled(20); } - function forceResolveChallenge(address[] calldata stakerA, address[] calldata stakerB) - external - override - whenPaused - { + function forceResolveChallenge( + address[] calldata stakerA, + address[] calldata stakerB + ) external override whenPaused { require(stakerA.length > 0, "EMPTY_ARRAY"); require(stakerA.length == stakerB.length, "WRONG_LENGTH"); for (uint256 i = 0; i < stakerA.length; i++) { diff --git a/src/rollup/RollupCreator.sol b/src/rollup/RollupCreator.sol index fca3f2456..8ef28af66 100644 --- a/src/rollup/RollupCreator.sol +++ b/src/rollup/RollupCreator.sol @@ -107,11 +107,9 @@ contract RollupCreator is Ownable { * - batchPosterManager The address which has the ability to rotate batch poster keys * @return The address of the newly created rollup */ - function createRollup(RollupDeploymentParams memory deployParams) - public - payable - returns (address) - { + function createRollup( + RollupDeploymentParams memory deployParams + ) public payable returns (address) { { // Make sure the immutable maxDataSize is as expected (, ISequencerInbox ethSequencerInbox, IInboxBase ethInbox, , ) = bridgeCreator @@ -139,12 +137,12 @@ contract RollupCreator is Ownable { // Create the rollup proxy to figure out the address and initialize it later RollupProxy rollup = new RollupProxy{salt: keccak256(abi.encode(deployParams))}(); - BridgeCreator.BridgeContracts memory bridgeContracts = bridgeCreator.createBridge( address(proxyAdmin), address(rollup), deployParams.nativeToken, - deployParams.config.sequencerInboxMaxTimeVariation + deployParams.config.sequencerInboxMaxTimeVariation, + deployParams.config.espressoTEEVerifier ); IChallengeManager challengeManager = IChallengeManager( @@ -232,10 +230,10 @@ contract RollupCreator is Ownable { return address(rollup); } - function _deployUpgradeExecutor(address rollupOwner, ProxyAdmin proxyAdmin) - internal - returns (address) - { + function _deployUpgradeExecutor( + address rollupOwner, + ProxyAdmin proxyAdmin + ) internal returns (address) { IUpgradeExecutor upgradeExecutor = IUpgradeExecutor( address( new TransparentUpgradeableProxy( @@ -306,7 +304,7 @@ contract RollupCreator is Ownable { zoltuCreate2Cost + erc1820Cost; } else if (decimals > 18) { - totalFeeNativeDenominated = totalFee * (10**(decimals - 18)); + totalFeeNativeDenominated = totalFee * (10 ** (decimals - 18)); } IERC20(_nativeToken).safeTransferFrom(msg.sender, _inbox, totalFeeNativeDenominated); @@ -316,16 +314,15 @@ contract RollupCreator is Ownable { } } - function _scaleDownToNativeDecimals(uint256 amount, uint8 decimals) - internal - pure - returns (uint256) - { + function _scaleDownToNativeDecimals( + uint256 amount, + uint8 decimals + ) internal pure returns (uint256) { uint256 scaledAmount = amount; if (decimals < 18) { - scaledAmount = amount / (10**(18 - decimals)); + scaledAmount = amount / (10 ** (18 - decimals)); // round up if necessary - if (scaledAmount * (10**(18 - decimals)) < amount) { + if (scaledAmount * (10 ** (18 - decimals)) < amount) { scaledAmount++; } } diff --git a/test/contract/arbRollup.spec.ts b/test/contract/arbRollup.spec.ts index 79281abce..1e6a24422 100644 --- a/test/contract/arbRollup.spec.ts +++ b/test/contract/arbRollup.spec.ts @@ -47,6 +47,8 @@ import { SequencerInbox, SequencerInbox__factory, Bridge, + EspressoTEEVerifierMock__factory, + EspressoTEEVerifierMock, } from '../../build/types' import { abi as UpgradeExecutorABI, @@ -72,6 +74,7 @@ import { constants, providers } from 'ethers' import { blockStateHash, MachineStatus } from './common/challengeLib' import * as globalStateLib from './common/globalStateLib' import { RollupChallengeStartedEvent } from '../../build/types/src/rollup/IRollupCore' +import { Address } from '@arbitrum/sdk' const zerobytes32 = ethers.constants.HashZero const stakeRequirement = 10 @@ -97,9 +100,10 @@ let admin: Signer let sequencer: Signer let challengeManager: ChallengeManager let upgradeExecutor: string -// let adminproxy: string +let espressoTEEVerifier: EspressoTEEVerifierMock async function getDefaultConfig( + espressoTEEVerifier: string, _confirmPeriodBlocks = confirmationPeriodBlocks ): Promise { return { @@ -119,6 +123,7 @@ async function getDefaultConfig( wasmModuleRoot: wasmModuleRoot, loserStakeEscrow: ZERO_ADDR, genesisBlockNum: 0, + espressoTEEVerifier, } } @@ -188,6 +193,14 @@ const setup = async () => { )) as Bridge__factory const ethBridge = await ethBridgeFac.deploy() + const espressoTEEVerifierFac = (await ethers.getContractFactory( + 'EspressoTEEVerifierMock' + )) as EspressoTEEVerifierMock__factory + + const espressoTEEVerifier = await espressoTEEVerifierFac.deploy() + + await espressoTEEVerifier.deployed() + const ethSequencerInboxFac = (await ethers.getContractFactory( 'SequencerInbox' )) as SequencerInbox__factory @@ -286,7 +299,7 @@ const setup = async () => { const maxFeePerGas = BigNumber.from('1000000000') const deployParams = { - config: await getDefaultConfig(), + config: await getDefaultConfig(espressoTEEVerifier.address), batchPosters: [await sequencer.getAddress()], validators: [ await val1.getAddress(), @@ -550,7 +563,7 @@ describe('ArbRollup', () => { await expect( rollupAdmin .connect(await impersonateAccount(upgradeExecutor)) - .initialize(await getDefaultConfig(), { + .initialize(await getDefaultConfig(espressoTEEVerifier.address), { challengeManager: constants.AddressZero, bridge: constants.AddressZero, inbox: constants.AddressZero, @@ -1363,18 +1376,21 @@ describe('ArbRollup', () => { ) const proxyPrimaryImpl = rollupAdminLogicFac.attach(proxyPrimaryTarget) await expect( - proxyPrimaryImpl.initialize(await getDefaultConfig(), { - challengeManager: constants.AddressZero, - bridge: constants.AddressZero, - inbox: constants.AddressZero, - outbox: constants.AddressZero, - rollupAdminLogic: constants.AddressZero, - rollupEventInbox: constants.AddressZero, - rollupUserLogic: constants.AddressZero, - sequencerInbox: constants.AddressZero, - validatorUtils: constants.AddressZero, - validatorWalletCreator: constants.AddressZero, - }) + proxyPrimaryImpl.initialize( + await getDefaultConfig(espressoTEEVerifier.address), + { + challengeManager: constants.AddressZero, + bridge: constants.AddressZero, + inbox: constants.AddressZero, + outbox: constants.AddressZero, + rollupAdminLogic: constants.AddressZero, + rollupEventInbox: constants.AddressZero, + rollupUserLogic: constants.AddressZero, + sequencerInbox: constants.AddressZero, + validatorUtils: constants.AddressZero, + validatorWalletCreator: constants.AddressZero, + } + ) ).to.be.revertedWith('Function must be called through delegatecall') }) @@ -1475,14 +1491,9 @@ describe('ArbRollup', () => { it('should fail the batch poster check', async function () { await expect( - sequencerInbox.addSequencerL2Batch( - 0, - '0x', - 0, - ethers.constants.AddressZero, - 0, - 0 - ) + sequencerInbox.functions[ + 'addSequencerL2Batch(uint256,bytes,uint256,address,uint256,uint256,bytes)' + ](0, '0x', 0, ethers.constants.AddressZero, 0, 0, '0x') ).to.revertedWith('NotBatchPoster') }) diff --git a/test/contract/sequencerInbox.spec.4844.ts b/test/contract/sequencerInbox.spec.4844.ts index c8752e74f..b75e3a943 100644 --- a/test/contract/sequencerInbox.spec.4844.ts +++ b/test/contract/sequencerInbox.spec.4844.ts @@ -23,6 +23,7 @@ import { expect } from 'chai' import { Bridge, Bridge__factory, + EspressoTEEVerifierMock__factory, GasRefunder__factory, Inbox, Inbox__factory, @@ -227,7 +228,11 @@ describe('SequencerInbox', async () => { ) const reader4844 = await Toolkit4844.deployReader4844(fundingWallet) - + const espressoTEEVerifierFac = new EspressoTEEVerifierMock__factory( + deployer + ) + const espressoTEEVerifier = await espressoTEEVerifierFac.deploy() + await espressoTEEVerifier.deployed() const sequencerInboxFac = new SequencerInbox__factory(deployer) const seqInboxTemplate = await sequencerInboxFac.deploy( 117964, @@ -252,6 +257,7 @@ describe('SequencerInbox', async () => { adminAddr, '0x' ) + const sequencerInboxProxy = await transparentUpgradeableProxyFac.deploy( seqInboxTemplate.address, adminAddr, @@ -270,19 +276,29 @@ describe('SequencerInbox', async () => { const bridgeAdmin = await bridgeFac .attach(bridgeProxy.address) .connect(rollupOwner) + const sequencerInbox = await sequencerInboxFac .attach(sequencerInboxProxy.address) .connect(user) await (await bridgeAdmin.initialize(rollupMock.address)).wait() + await ( - await sequencerInbox.initialize(bridgeProxy.address, { - delayBlocks: maxDelayBlocks, - delaySeconds: maxDelayTime, - futureBlocks: 10, - futureSeconds: 3000, - }) + await sequencerInbox + .connect(user) + .functions[ + 'initialize(address,(uint256,uint256,uint256,uint256),address)' + ]( + bridgeProxy.address, + { + delayBlocks: maxDelayBlocks, + delaySeconds: maxDelayTime, + futureBlocks: 10, + futureSeconds: 3000, + }, + espressoTEEVerifier.address, + { gasLimit: 10000000 } + ) ).wait() - const inbox = await inboxFac.attach(inboxProxy.address).connect(user) await ( @@ -366,14 +382,15 @@ describe('SequencerInbox', async () => { await sequencerInbox .connect(batchPoster) .functions[ - 'addSequencerL2BatchFromOrigin(uint256,bytes,uint256,address,uint256,uint256)' + 'addSequencerL2BatchFromOrigin(uint256,bytes,uint256,address,uint256,uint256,bytes)' ]( await bridge.sequencerMessageCount(), '0x0042', await bridge.delayedMessageCount(), gasRefunder.address, subMessageCount, - subMessageCount.add(1) + subMessageCount.add(1), + '0x' ) ).wait() expect((await batchPoster.getBalance()).gt(balBefore), 'Refund not enough') diff --git a/test/contract/sequencerInboxForceInclude.spec.ts b/test/contract/sequencerInboxForceInclude.spec.ts index fcaf20558..7ad945d4f 100644 --- a/test/contract/sequencerInboxForceInclude.spec.ts +++ b/test/contract/sequencerInboxForceInclude.spec.ts @@ -23,6 +23,7 @@ import { expect } from 'chai' import { Bridge, Bridge__factory, + EspressoTEEVerifierMock__factory, Inbox, Inbox__factory, MessageTester, @@ -247,6 +248,13 @@ describe('SequencerInboxForceInclude', async () => { 'Bridge' )) as Bridge__factory const bridgeTemplate = await bridgeFac.deploy() + + const espressoTEEVerifierInboxFac = (await ethers.getContractFactory( + 'EspressoTEEVerifierMock' + )) as EspressoTEEVerifierMock__factory + const espressoTEEVerifier = await espressoTEEVerifierInboxFac.deploy() + await espressoTEEVerifier.deployed() + const transparentUpgradeableProxyFac = (await ethers.getContractFactory( 'TransparentUpgradeableProxy' )) as TransparentUpgradeableProxy__factory @@ -256,6 +264,7 @@ describe('SequencerInboxForceInclude', async () => { adminAddr, '0x' ) + const sequencerInboxProxy = await transparentUpgradeableProxyFac.deploy( seqInboxTemplate.address, adminAddr, @@ -270,17 +279,29 @@ describe('SequencerInboxForceInclude', async () => { const bridgeAdmin = await bridgeFac .attach(bridgeProxy.address) .connect(rollupOwner) + const sequencerInbox = await sequencerInboxFac .attach(sequencerInboxProxy.address) .connect(user) await bridge.initialize(rollup.address) - await sequencerInbox.initialize(bridgeProxy.address, { - delayBlocks: maxDelayBlocks, - delaySeconds: maxDelayTime, - futureBlocks: 10, - futureSeconds: 3000, - }) + await ( + await sequencerInbox + .connect(user) + .functions[ + 'initialize(address,(uint256,uint256,uint256,uint256),address)' + ]( + bridgeProxy.address, + { + delayBlocks: maxDelayBlocks, + delaySeconds: maxDelayTime, + futureBlocks: 10, + futureSeconds: 3000, + }, + espressoTEEVerifier.address, + { gasLimit: 10000000 } + ) + ).wait() await ( await sequencerInbox @@ -344,7 +365,7 @@ describe('SequencerInboxForceInclude', async () => { await sequencerInbox .connect(batchPoster) .functions[ - 'addSequencerL2BatchFromOrigin(uint256,bytes,uint256,address,uint256,uint256)' + 'addSequencerL2BatchFromOrigin(uint256,bytes,uint256,address,uint256,uint256,bytes)' ]( 0, data, @@ -352,6 +373,7 @@ describe('SequencerInboxForceInclude', async () => { ethers.constants.AddressZero, seqReportedMessageSubCount, seqReportedMessageSubCount.add(10), + '0x', { gasLimit: 10000000 } ) ).wait() @@ -396,14 +418,15 @@ describe('SequencerInboxForceInclude', async () => { await sequencerInbox .connect(batchPoster) [ - 'addSequencerL2BatchFromOrigin(uint256,bytes,uint256,address,uint256,uint256)' + 'addSequencerL2BatchFromOrigin(uint256,bytes,uint256,address,uint256,uint256,bytes)' ]( 0, '0x', 0, ethers.constants.AddressZero, 0, - ethers.constants.MaxUint256 + ethers.constants.MaxUint256, + '0x' ) const delayedTx = await sendDelayedTx( diff --git a/test/e2e/orbitChain.ts b/test/e2e/orbitChain.ts index 060a0f7d9..0eb9c979b 100644 --- a/test/e2e/orbitChain.ts +++ b/test/e2e/orbitChain.ts @@ -15,6 +15,7 @@ import { ERC20, ERC20Inbox__factory, ERC20__factory, + EspressoTEEVerifierMock__factory, EthVault__factory, IERC20Bridge__factory, IInbox__factory, @@ -811,6 +812,22 @@ describe('Orbit Chain', () => { ) } + const batchPosters = [ethers.Wallet.createRandom().address] + const batchPosterManager = ethers.Wallet.createRandom().address + const validators = [ethers.Wallet.createRandom().address] + const maxDataSize = 104857 + const nativeTokenAddress = nativeToken + ? nativeToken.address + : ethers.constants.AddressZero + const deployFactoriesToL2 = true + const maxFeePerGasForRetryables = BigNumber.from('100000000') // 0.1 gwei + const espressoTEEVerifierFac = (await hardhatEthers.getContractFactory( + 'EspressoTEEVerifierMock' + )) as EspressoTEEVerifierMock__factory + const espressoTEEVerifier = await espressoTEEVerifierFac.deploy() + + await espressoTEEVerifier.deployed() + /// deploy params const config = { confirmPeriodBlocks: ethers.BigNumber.from('150'), @@ -831,17 +848,8 @@ describe('Orbit Chain', () => { delaySeconds: ethers.BigNumber.from('86400'), futureSeconds: ethers.BigNumber.from('3600'), }, + espressoTEEVerifier: espressoTEEVerifier.address, } - const batchPosters = [ethers.Wallet.createRandom().address] - const batchPosterManager = ethers.Wallet.createRandom().address - const validators = [ethers.Wallet.createRandom().address] - const maxDataSize = 104857 - const nativeTokenAddress = nativeToken - ? nativeToken.address - : ethers.constants.AddressZero - const deployFactoriesToL2 = true - const maxFeePerGasForRetryables = BigNumber.from('100000000') // 0.1 gwei - const deployParams = { config, batchPosters, diff --git a/test/foundry/BridgeCreator.t.sol b/test/foundry/BridgeCreator.t.sol index f6178d769..179ffab44 100644 --- a/test/foundry/BridgeCreator.t.sol +++ b/test/foundry/BridgeCreator.t.sol @@ -8,13 +8,13 @@ import "../../src/bridge/ISequencerInbox.sol"; import "../../src/bridge/AbsInbox.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol"; +import {EspressoTEEVerifierMock} from "../../src/mocks/EspressoTEEVerifier.sol"; contract BridgeCreatorTest is Test { BridgeCreator public creator; address public owner = address(100); uint256 public constant MAX_DATA_SIZE = 117_964; IReader4844 dummyReader4844 = IReader4844(address(137)); - BridgeCreator.BridgeContracts ethBasedTemplates = BridgeCreator.BridgeContracts({ bridge: new Bridge(), @@ -124,11 +124,14 @@ contract BridgeCreatorTest is Test { ); timeVars.delayBlocks; + EspressoTEEVerifierMock espressoTEEVerifier = new EspressoTEEVerifierMock(); + BridgeCreator.BridgeContracts memory contracts = creator.createBridge( proxyAdmin, rollup, nativeToken, - timeVars + timeVars, + address(espressoTEEVerifier) ); ( IBridge bridge, @@ -195,11 +198,14 @@ contract BridgeCreatorTest is Test { ); timeVars.delayBlocks; // TODO: what is this? + EspressoTEEVerifierMock espressoTEEVerifier = new EspressoTEEVerifierMock(); + BridgeCreator.BridgeContracts memory contracts = creator.createBridge( proxyAdmin, rollup, nativeToken, - timeVars + timeVars, + address(espressoTEEVerifier) ); ( IBridge bridge, diff --git a/test/foundry/ChallengeManager.t.sol b/test/foundry/ChallengeManager.t.sol index 88557db99..1e9622e22 100644 --- a/test/foundry/ChallengeManager.t.sol +++ b/test/foundry/ChallengeManager.t.sol @@ -24,10 +24,14 @@ contract ChallengeManagerTest is Test { ); chalman.initialize(resultReceiver, sequencerInbox, bridge, osp); assertEq( - address(chalman.resultReceiver()), address(resultReceiver), "Result receiver not set" + address(chalman.resultReceiver()), + address(resultReceiver), + "Result receiver not set" ); assertEq( - address(chalman.sequencerInbox()), address(sequencerInbox), "Sequencer inbox not set" + address(chalman.sequencerInbox()), + address(sequencerInbox), + "Sequencer inbox not set" ); assertEq(address(chalman.bridge()), address(bridge), "Bridge not set"); assertEq(address(chalman.osp()), address(osp), "OSP not set"); @@ -52,10 +56,12 @@ contract ChallengeManagerTest is Test { // legacy hashes bytes32 legacySegment0 = legacyOSP.getStartMachineHash( - keccak256(abi.encodePacked("globalStateHash[0]")), legacyRoot + keccak256(abi.encodePacked("globalStateHash[0]")), + legacyRoot ); bytes32 legacySegment1 = legacyOSP.getEndMachineHash( - MachineStatus.FINISHED, keccak256(abi.encodePacked("globalStateHashes[1]")) + MachineStatus.FINISHED, + keccak256(abi.encodePacked("globalStateHashes[1]")) ); /// new OSP @@ -72,10 +78,12 @@ contract ChallengeManagerTest is Test { // new hashes bytes32 newSegment0 = _newOSP.getStartMachineHash( - keccak256(abi.encodePacked("globalStateHash[0]")), randomRoot + keccak256(abi.encodePacked("globalStateHash[0]")), + randomRoot ); bytes32 newSegment1 = _newOSP.getEndMachineHash( - MachineStatus.FINISHED, keccak256(abi.encodePacked("new_globalStateHashes[1]")) + MachineStatus.FINISHED, + keccak256(abi.encodePacked("new_globalStateHashes[1]")) ); /// do upgrade @@ -83,7 +91,10 @@ contract ChallengeManagerTest is Test { TransparentUpgradeableProxy(payable(address(chalman))).upgradeToAndCall( address(chalmanImpl), abi.encodeWithSelector( - ChallengeManager.postUpgradeInit.selector, _newOSP, legacyRoot, legacyOSP + ChallengeManager.postUpgradeInit.selector, + _newOSP, + legacyRoot, + legacyOSP ) ); @@ -92,14 +103,16 @@ contract ChallengeManagerTest is Test { assertEq(address(_condOsp), address(legacyOSP), "Legacy osp not set"); assertEq( _condOsp.getStartMachineHash( - keccak256(abi.encodePacked("globalStateHash[0]")), legacyRoot + keccak256(abi.encodePacked("globalStateHash[0]")), + legacyRoot ), legacySegment0, "Unexpected start machine hash" ); assertEq( _condOsp.getEndMachineHash( - MachineStatus.FINISHED, keccak256(abi.encodePacked("globalStateHashes[1]")) + MachineStatus.FINISHED, + keccak256(abi.encodePacked("globalStateHashes[1]")) ), legacySegment1, "Unexpected end machine hash" @@ -110,14 +123,16 @@ contract ChallengeManagerTest is Test { assertEq(address(_newOsp), address(_newOSP), "New osp not set"); assertEq( _newOsp.getStartMachineHash( - keccak256(abi.encodePacked("globalStateHash[0]")), randomRoot + keccak256(abi.encodePacked("globalStateHash[0]")), + randomRoot ), newSegment0, "Unexpected start machine hash" ); assertEq( _newOsp.getEndMachineHash( - MachineStatus.FINISHED, keccak256(abi.encodePacked("new_globalStateHashes[1]")) + MachineStatus.FINISHED, + keccak256(abi.encodePacked("new_globalStateHashes[1]")) ), newSegment1, "Unexpected end machine hash" @@ -135,7 +150,10 @@ contract ChallengeManagerTest is Test { TransparentUpgradeableProxy(payable(address(chalman))).upgradeToAndCall( address(chalmanImpl), abi.encodeWithSelector( - ChallengeManager.postUpgradeInit.selector, newOsp, randomRoot, condOsp + ChallengeManager.postUpgradeInit.selector, + newOsp, + randomRoot, + condOsp ) ); diff --git a/test/foundry/EspressoTEEVerifier.t.sol b/test/foundry/EspressoTEEVerifier.t.sol new file mode 100644 index 000000000..30e890795 --- /dev/null +++ b/test/foundry/EspressoTEEVerifier.t.sol @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +import "forge-std/Test.sol"; +import {EspressoTEEVerifier, IEspressoTEEVerifier} from "../../src/bridge/EspressoTEEVerifier.sol"; + +contract EspressoTEEVerifierTest is Test { + address proxyAdmin = address(140); + address adminTEE = address(141); + address fakeAddress = address(145); + + EspressoTEEVerifier espressoTEEVerifier; + bytes32 reportDataHash = + bytes32(0x739f5f48d929cc121c080ec6527a22be3c69bad5c40606cd098a9fa7ed971f1b); + bytes32 mrEnclave = bytes32(0x51dfe95acffa8a4075b716257c836895af9202a5fd56c8c2208dacb79c659ff0); + bytes32 mrSigner = bytes32(0x0c8242bba090f54b10de0c2d1ca4b633b9c08b7178451c71d737c214b72fc836); + // Address of the automata V3QuoteVerifier deployed on sepolia + address v3QuoteVerifier = address(0x6E64769A13617f528a2135692484B681Ee1a7169); + + function setUp() public { + vm.createSelectFork("https://rpc.ankr.com/eth_sepolia"); + // Get the instance of the DCAP Attestation QuoteVerifier on the Arbitrum Sepolia Rollup + vm.startPrank(adminTEE); + espressoTEEVerifier = new EspressoTEEVerifier(mrEnclave, mrSigner, v3QuoteVerifier); + vm.stopPrank(); + } + + function testVerifyQuoteValid() public { + vm.startPrank(adminTEE); + string memory quotePath = "/test/foundry/configs/attestation.bin"; + string memory inputFile = string.concat(vm.projectRoot(), quotePath); + bytes memory sampleQuote = vm.readFileBinary(inputFile); + espressoTEEVerifier.verify(sampleQuote, reportDataHash); + vm.stopPrank(); + } + + function testVerifyInvalidHeaderInQuote() public { + string memory quotePath = "/test/foundry/configs/incorrect_header_in_quote.bin"; + string memory inputFile = string.concat(vm.projectRoot(), quotePath); + bytes memory invalidQuote = vm.readFileBinary(inputFile); + vm.expectRevert(IEspressoTEEVerifier.InvalidHeaderVersion.selector); + espressoTEEVerifier.verify(invalidQuote, reportDataHash); + } + + function testVerifyInvalidQuote() public { + string memory quotePath = "/test/foundry/configs/invalid_quote.bin"; + string memory inputFile = string.concat(vm.projectRoot(), quotePath); + bytes memory invalidQuote = vm.readFileBinary(inputFile); + vm.expectRevert(IEspressoTEEVerifier.InvalidQuote.selector); + espressoTEEVerifier.verify(invalidQuote, reportDataHash); + } + + /** + Test incorrect report data hash + */ + function testIncorrectReportDataHash() public { + vm.startPrank(adminTEE); + string memory quotePath = "/test/foundry/configs/attestation.bin"; + string memory inputFile = string.concat(vm.projectRoot(), quotePath); + bytes memory sampleQuote = vm.readFileBinary(inputFile); + vm.expectRevert(IEspressoTEEVerifier.InvalidReportDataHash.selector); + espressoTEEVerifier.verify(sampleQuote, bytes32(0)); + } + + function testIncorrectMrEnclave() public { + vm.startPrank(adminTEE); + string memory quotePath = "/test/foundry/configs/attestation.bin"; + string memory inputFile = string.concat(vm.projectRoot(), quotePath); + bytes memory sampleQuote = vm.readFileBinary(inputFile); + bytes32 incorrectMrEnclave = bytes32( + 0x51dfe95acffa8a4075b716257c836895af9202a5fd56c8c2208dacb79c659ff1 + ); + espressoTEEVerifier = new EspressoTEEVerifier( + incorrectMrEnclave, + mrSigner, + v3QuoteVerifier + ); + vm.expectRevert(IEspressoTEEVerifier.InvalidMREnclaveOrSigner.selector); + espressoTEEVerifier.verify(sampleQuote, reportDataHash); + } + + function testIncorrectMrSigner() public { + vm.startPrank(adminTEE); + string memory quotePath = "/test/foundry/configs/attestation.bin"; + string memory inputFile = string.concat(vm.projectRoot(), quotePath); + bytes memory sampleQuote = vm.readFileBinary(inputFile); + bytes32 incorrectMrSigner = bytes32( + 0x51dfe95acffa8a4075b716257c836895af9202a5fd56c8c2208dacb79c659ff5 + ); + espressoTEEVerifier = new EspressoTEEVerifier( + mrEnclave, + incorrectMrSigner, + v3QuoteVerifier + ); + vm.expectRevert(IEspressoTEEVerifier.InvalidMREnclaveOrSigner.selector); + espressoTEEVerifier.verify(sampleQuote, reportDataHash); + } + + function testSetMrEnclave() public { + vm.startPrank(adminTEE); + bytes32 newMrEnclave = bytes32(hex"01"); + espressoTEEVerifier.setMrEnclave(newMrEnclave); + assertEq(espressoTEEVerifier.mrEnclave(), newMrEnclave); + vm.stopPrank(); + } + + function testSetMrSigner() public { + vm.startPrank(adminTEE); + bytes32 newMrSigner = bytes32(hex"01"); + espressoTEEVerifier.setMrSigner(newMrSigner); + assertEq(espressoTEEVerifier.mrSigner(), newMrSigner); + vm.stopPrank(); + } +} diff --git a/test/foundry/ExpressLaneAuction.t.sol b/test/foundry/ExpressLaneAuction.t.sol index 31652b752..99150f0cc 100644 --- a/test/foundry/ExpressLaneAuction.t.sol +++ b/test/foundry/ExpressLaneAuction.t.sol @@ -1377,7 +1377,7 @@ contract ExpressLaneAuctionTest is Test { // bad v means invalid sig // vm.expectRevert(ECDSAInvalidSignature.selector); - vm.expectRevert(abi.encodePacked("ECDSA: invalid signature 'v' value")); + vm.expectRevert(abi.encodePacked("ECDSA: invalid signature")); rs.auction.resolveMultiBidAuction(bid1, rs.bid0); bytes32 h0 = rs.auction.getBidHash( @@ -1394,12 +1394,12 @@ contract ExpressLaneAuctionTest is Test { // bad v means invalid sig // vm.expectRevert(ECDSAInvalidSignature.selector); - vm.expectRevert(abi.encodePacked("ECDSA: invalid signature 'v' value")); + vm.expectRevert(abi.encodePacked("ECDSA: invalid signature")); rs.auction.resolveMultiBidAuction(rs.bid1, bid0); // bad v means invalid sig // vm.expectRevert(ECDSAInvalidSignature.selector); - vm.expectRevert(abi.encodePacked("ECDSA: invalid signature 'v' value")); + vm.expectRevert(abi.encodePacked("ECDSA: invalid signature")); rs.auction.resolveSingleBidAuction(bid0); } diff --git a/test/foundry/RollupCreator.t.sol b/test/foundry/RollupCreator.t.sol index 62a1262e5..7db89ff9f 100644 --- a/test/foundry/RollupCreator.t.sol +++ b/test/foundry/RollupCreator.t.sol @@ -19,6 +19,7 @@ import "../../src/rollup/DeployHelper.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol"; +import {EspressoTEEVerifierMock} from "../../src/mocks/EspressoTEEVerifier.sol"; contract MockHotShot { mapping(uint256 => uint256) public commitments; @@ -103,7 +104,7 @@ contract RollupCreatorTest is Test { function test_createEthRollup() public { vm.startPrank(deployer); - + address proxyAdmin = address(140); // deployment params ISequencerInbox.MaxTimeVariation memory timeVars = ISequencerInbox.MaxTimeVariation( ((60 * 60 * 24) / 15), @@ -111,6 +112,9 @@ contract RollupCreatorTest is Test { 60 * 60 * 24, 60 * 60 ); + + EspressoTEEVerifierMock espressoTEEVerifier = new EspressoTEEVerifierMock(); + Config memory config = Config({ confirmPeriodBlocks: 20, extraChallengeTimeBlocks: 200, @@ -122,7 +126,8 @@ contract RollupCreatorTest is Test { chainId: 1337, chainConfig: "abc", genesisBlockNum: 15_000_000, - sequencerInboxMaxTimeVariation: timeVars + sequencerInboxMaxTimeVariation: timeVars, + espressoTEEVerifier: address(espressoTEEVerifier) }); // prepare funds @@ -255,6 +260,7 @@ contract RollupCreatorTest is Test { function test_createErc20Rollup() public { vm.startPrank(deployer); + address proxyAdmin = address(140); address nativeToken = address( new ERC20PresetFixedSupply("Appchain Token", "App", 1_000_000 ether, deployer) ); @@ -266,6 +272,9 @@ contract RollupCreatorTest is Test { 60 * 60 * 24, 60 * 60 ); + + EspressoTEEVerifierMock espressoTEEVerifier = new EspressoTEEVerifierMock(); + Config memory config = Config({ confirmPeriodBlocks: 20, extraChallengeTimeBlocks: 200, @@ -277,7 +286,8 @@ contract RollupCreatorTest is Test { chainId: 1337, chainConfig: "abc", genesisBlockNum: 15_000_000, - sequencerInboxMaxTimeVariation: timeVars + sequencerInboxMaxTimeVariation: timeVars, + espressoTEEVerifier: address(espressoTEEVerifier) }); // approve fee token to pay for deployment of L2 factories @@ -413,7 +423,6 @@ contract RollupCreatorTest is Test { function test_upgrade() public { vm.startPrank(deployer); - // deployment params ISequencerInbox.MaxTimeVariation memory timeVars = ISequencerInbox.MaxTimeVariation( ((60 * 60 * 24) / 15), @@ -421,6 +430,9 @@ contract RollupCreatorTest is Test { 60 * 60 * 24, 60 * 60 ); + + EspressoTEEVerifierMock espressoTEEVerifier = new EspressoTEEVerifierMock(); + Config memory config = Config({ confirmPeriodBlocks: 20, extraChallengeTimeBlocks: 200, @@ -432,7 +444,8 @@ contract RollupCreatorTest is Test { chainId: 1337, chainConfig: "abc", genesisBlockNum: 15_000_000, - sequencerInboxMaxTimeVariation: timeVars + sequencerInboxMaxTimeVariation: timeVars, + espressoTEEVerifier: address(espressoTEEVerifier) }); // prepare funds @@ -537,11 +550,7 @@ contract RollupCreatorTest is Test { } contract ProxyUpgradeAction { - function perform( - address admin, - address payable target, - address newLogic - ) public payable { + function perform(address admin, address payable target, address newLogic) public payable { ProxyAdmin(admin).upgrade(TransparentUpgradeableProxy(target), newLogic); } } diff --git a/test/foundry/SequencerInbox.t.sol b/test/foundry/SequencerInbox.t.sol index 92902d687..aebe30f31 100644 --- a/test/foundry/SequencerInbox.t.sol +++ b/test/foundry/SequencerInbox.t.sol @@ -7,6 +7,13 @@ import "../../src/bridge/Bridge.sol"; import "../../src/bridge/SequencerInbox.sol"; import {ERC20Bridge} from "../../src/bridge/ERC20Bridge.sol"; import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol"; +import {EspressoTEEVerifierMock} from "../../src/mocks/EspressoTEEVerifier.sol"; +import { + TransparentUpgradeableProxy +} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { + V3QuoteVerifier +} from "@automata-network/dcap-attestation/contracts/verifiers/V3QuoteVerifier.sol"; contract RollupMock { address public immutable owner; @@ -52,9 +59,28 @@ contract SequencerInboxTest is Test { }); address dummyInbox = address(139); address proxyAdmin = address(140); + bytes32 mrEnclave = bytes32(0x51dfe95acffa8a4075b716257c836895af9202a5fd56c8c2208dacb79c659ff0); + bytes32 mrSigner = bytes32(0x0c8242bba090f54b10de0c2d1ca4b633b9c08b7178451c71d737c214b72fc836); IReader4844 dummyReader4844 = IReader4844(address(137)); uint256 public constant MAX_DATA_SIZE = 117964; + address adminTEE = address(141); + address fakeAddress = address(145); + + EspressoTEEVerifierMock espressoTEEVerifier; + V3QuoteVerifier quoteVerifier; + bytes sampleQuote; + + function setUp() public { + vm.startPrank(adminTEE); + + espressoTEEVerifier = new EspressoTEEVerifierMock(); + + string memory quotePath = "/test/foundry/configs/attestation.bin"; + string memory inputFile = string.concat(vm.projectRoot(), quotePath); + sampleQuote = vm.readFileBinary(inputFile); + vm.stopPrank(); + } function deployRollup(bool isArbHosted) internal returns (SequencerInbox, Bridge) { RollupMock rollupMock = new RollupMock(rollupOwner); @@ -75,7 +101,7 @@ contract SequencerInboxTest is Test { SequencerInbox seqInbox = SequencerInbox( address(new TransparentUpgradeableProxy(address(seqInboxImpl), proxyAdmin, "")) ); - seqInbox.initialize(bridge, maxTimeVariation); + seqInbox.initialize(bridge, maxTimeVariation, address(espressoTEEVerifier)); vm.prank(rollupOwner); seqInbox.setIsBatchPoster(tx.origin, true); @@ -104,6 +130,7 @@ contract SequencerInboxTest is Test { abi.encodeWithSelector(ArbSys.arbOSVersion.selector), abi.encode(uint256(11)) ); + SequencerInbox seqInboxImpl = new SequencerInbox( maxDataSize, IReader4844(address(0)), @@ -112,7 +139,7 @@ contract SequencerInboxTest is Test { SequencerInbox seqInbox = SequencerInbox( address(new TransparentUpgradeableProxy(address(seqInboxImpl), proxyAdmin, "")) ); - seqInbox.initialize(bridge, maxTimeVariation); + seqInbox.initialize(bridge, maxTimeVariation, address(espressoTEEVerifier)); vm.prank(rollupOwner); seqInbox.setIsBatchPoster(tx.origin, true); @@ -237,13 +264,18 @@ contract SequencerInboxTest is Test { expectEvents(bridge, seqInbox, data, false, false); vm.prank(tx.origin); + string memory quotePath = "/test/foundry/configs/attestation.bin"; + string memory inputFile = string.concat(vm.projectRoot(), quotePath); + sampleQuote = vm.readFileBinary(inputFile); + seqInbox.addSequencerL2BatchFromOrigin( sequenceNumber, data, delayedMessagesRead, IGasRefunder(address(0)), subMessageCount, - subMessageCount + 1 + subMessageCount + 1, + sampleQuote ); } @@ -280,7 +312,7 @@ contract SequencerInboxTest is Test { address seqInboxLogic = address(new SequencerInbox(MAX_DATA_SIZE, dummyReader4844, false)); SequencerInbox seqInboxProxy = SequencerInbox(TestUtil.deployProxy(seqInboxLogic)); - seqInboxProxy.initialize(IBridge(_bridge), maxTimeVariation); + seqInboxProxy.initialize(IBridge(_bridge), maxTimeVariation, address(espressoTEEVerifier)); assertEq(seqInboxProxy.isUsingFeeToken(), false, "Invalid isUsingFeeToken"); assertEq(address(seqInboxProxy.bridge()), address(_bridge), "Invalid bridge"); @@ -296,7 +328,7 @@ contract SequencerInboxTest is Test { address seqInboxLogic = address(new SequencerInbox(MAX_DATA_SIZE, dummyReader4844, true)); SequencerInbox seqInboxProxy = SequencerInbox(TestUtil.deployProxy(seqInboxLogic)); - seqInboxProxy.initialize(IBridge(_bridge), maxTimeVariation); + seqInboxProxy.initialize(IBridge(_bridge), maxTimeVariation, address(espressoTEEVerifier)); assertEq(seqInboxProxy.isUsingFeeToken(), true, "Invalid isUsingFeeToken"); assertEq(address(seqInboxProxy.bridge()), address(_bridge), "Invalid bridge"); @@ -308,12 +340,11 @@ contract SequencerInboxTest is Test { address(new TransparentUpgradeableProxy(address(new Bridge()), proxyAdmin, "")) ); _bridge.initialize(IOwnable(address(new RollupMock(rollupOwner)))); - address seqInboxLogic = address(new SequencerInbox(MAX_DATA_SIZE, dummyReader4844, true)); SequencerInbox seqInboxProxy = SequencerInbox(TestUtil.deployProxy(seqInboxLogic)); vm.expectRevert(abi.encodeWithSelector(NativeTokenMismatch.selector)); - seqInboxProxy.initialize(IBridge(_bridge), maxTimeVariation); + seqInboxProxy.initialize(IBridge(_bridge), maxTimeVariation, address(espressoTEEVerifier)); } function testInitialize_revert_NativeTokenMismatch_FeeTokenEth() public { @@ -322,12 +353,11 @@ contract SequencerInboxTest is Test { ); address nativeToken = address(new ERC20PresetMinterPauser("Appchain Token", "App")); _bridge.initialize(IOwnable(address(new RollupMock(rollupOwner))), nativeToken); - address seqInboxLogic = address(new SequencerInbox(MAX_DATA_SIZE, dummyReader4844, false)); SequencerInbox seqInboxProxy = SequencerInbox(TestUtil.deployProxy(seqInboxLogic)); vm.expectRevert(abi.encodeWithSelector(NativeTokenMismatch.selector)); - seqInboxProxy.initialize(IBridge(_bridge), maxTimeVariation); + seqInboxProxy.initialize(IBridge(_bridge), maxTimeVariation, address(espressoTEEVerifier)); } function testAddSequencerL2BatchFromOrigin_ArbitrumHosted() public { @@ -354,13 +384,15 @@ contract SequencerInboxTest is Test { expectEvents(bridge, seqInbox, data, true, false); vm.prank(tx.origin); + seqInbox.addSequencerL2BatchFromOrigin( sequenceNumber, data, delayedMessagesRead, IGasRefunder(address(0)), subMessageCount, - subMessageCount + 1 + subMessageCount + 1, + sampleQuote ); } @@ -385,13 +417,15 @@ contract SequencerInboxTest is Test { expectEvents(IBridge(address(bridge)), seqInbox, data, true, true); vm.prank(tx.origin); + seqInbox.addSequencerL2BatchFromOrigin( sequenceNumber, data, delayedMessagesRead, IGasRefunder(address(0)), subMessageCount, - subMessageCount + 1 + subMessageCount + 1, + sampleQuote ); } @@ -410,13 +444,15 @@ contract SequencerInboxTest is Test { uint256 delayedMessagesRead = bridge.delayedMessageCount(); vm.expectRevert(abi.encodeWithSelector(NotOrigin.selector)); + seqInbox.addSequencerL2BatchFromOrigin( sequenceNumber, data, delayedMessagesRead, IGasRefunder(address(0)), subMessageCount, - subMessageCount + 1 + subMessageCount + 1, + sampleQuote ); vm.prank(rollupOwner); @@ -430,7 +466,8 @@ contract SequencerInboxTest is Test { delayedMessagesRead, IGasRefunder(address(0)), subMessageCount, - subMessageCount + 1 + subMessageCount + 1, + sampleQuote ); vm.prank(rollupOwner); @@ -454,7 +491,8 @@ contract SequencerInboxTest is Test { delayedMessagesRead, IGasRefunder(address(0)), subMessageCount, - subMessageCount + 1 + subMessageCount + 1, + sampleQuote ); bytes memory authenticatedData = bytes.concat(seqInbox.DATA_BLOB_HEADER_FLAG(), data); @@ -466,7 +504,8 @@ contract SequencerInboxTest is Test { delayedMessagesRead, IGasRefunder(address(0)), subMessageCount, - subMessageCount + 1 + subMessageCount + 1, + sampleQuote ); vm.expectRevert( @@ -479,14 +518,14 @@ contract SequencerInboxTest is Test { delayedMessagesRead, IGasRefunder(address(0)), subMessageCount, - subMessageCount + 1 + subMessageCount + 1, + sampleQuote ); } function testPostUpgradeInitAlreadyInit() public returns (SequencerInbox, SequencerInbox) { (SequencerInbox seqInbox, ) = deployRollup(false); SequencerInbox seqInboxImpl = new SequencerInbox(maxDataSize, dummyReader4844, false); - vm.expectRevert(abi.encodeWithSelector(AlreadyInit.selector)); vm.prank(proxyAdmin); TransparentUpgradeableProxy(payable(address(seqInbox))).upgradeToAndCall( diff --git a/test/foundry/SequencerInboxTEE.t.sol b/test/foundry/SequencerInboxTEE.t.sol new file mode 100644 index 000000000..7139f6e71 --- /dev/null +++ b/test/foundry/SequencerInboxTEE.t.sol @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.4; + +import "forge-std/Test.sol"; +import "./util/TestUtil.sol"; +import "../../src/bridge/Bridge.sol"; +import "../../src/bridge/SequencerInbox.sol"; +import {ERC20Bridge} from "../../src/bridge/ERC20Bridge.sol"; +import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol"; +import {EspressoTEEVerifier} from "../../src/bridge/EspressoTEEVerifier.sol"; +import { + TransparentUpgradeableProxy +} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import { + V3QuoteVerifier +} from "@automata-network/dcap-attestation/contracts/verifiers/V3QuoteVerifier.sol"; + +contract RollupMock { + address public immutable owner; + + constructor(address _owner) { + owner = _owner; + } +} + +contract SequencerInboxTest is Test { + event TEEAttestationQuoteVerified(uint256 indexed seqMessageIndex); + error InvalidReportDataHash(); + + address rollupOwner = address(137); + uint256 maxDataSize = 10000; + ISequencerInbox.MaxTimeVariation maxTimeVariation = + ISequencerInbox.MaxTimeVariation({ + delayBlocks: 10, + futureBlocks: 10, + delaySeconds: 100, + futureSeconds: 100 + }); + address dummyInbox = address(139); + address proxyAdmin = address(140); + bytes32 mrEnclave = bytes32(0x32ed2f272a3bb58e07dc8af2e66879a57c648549707cbeb396ffc234ba1b65d9); + bytes32 mrSigner = bytes32(0x0458a0e62674775ca9a048016f817f39b0bd40153000aceb44a5128ded30555e); + IReader4844 dummyReader4844 = IReader4844(address(137)); + + uint256 public constant MAX_DATA_SIZE = 117964; + address adminTEE = address(141); + address fakeAddress = address(145); + + EspressoTEEVerifier espressoTEEVerifier; + V3QuoteVerifier quoteVerifier; + bytes sampleQuote; + SequencerInbox seqInboxImpl; + SequencerInbox seqInbox; + Bridge bridgeImpl; + Bridge bridge; + RollupMock rollupMock; + // Address of the automata V3QuoteVerifier deployed on sepolia + address v3QuoteVerifier = address(0x6E64769A13617f528a2135692484B681Ee1a7169); + // address of the Reader4844 contract deployed on sepolia + address reader4844 = address(0xf6134C5849Fe8177163747288d41283B271B1624); + + // This was generated by running the batch poster in the TEE + // and coping the l2MessageData that it was trying to post in a batch + bytes l2TEEData = + hex"001ba63251c4180700b07ea0882a5101f879601b331f3c180d5d7ea1bf30d808701425f6ed6175ca56a48c90647678dae6df016225209cfb32dde6e21bb5afdfe15c3530b89bb2780fb316cd0560ff3d62d188ab7695e93afe9cfea76a39ff744e95a99d252e484ea10c21b5048622668e0d9ed74d731d3e0092d23a7d8092f65a8fabcee32a37b5ab58a5f5ffd63445e3386c9a496651efad4b9ccd024dcba7ae9c83e7bd55e1ff971480258d5d40696ca708873bd0e184c73d01f688105b50c48d780fcfb2d634b5bc693c2ac1220c298180a217e0ff7b9de585ffb2acdd001f5089459b19ef115445264523bdb75ff4bd8e259be5e4b4d6b21675e875c0b03ef20518ab9b54807600a0042ec31d1054295394579429ebd82662743b63b2b64955e38d010fbf0ca713b64ef4d5282184de57dee9cf98f9c6dc7ef71b38017371e7744cf07e4ce2848377d3c6f4bc682c658ffc8805b59bbd7fd8b8a1efba0d6ea72c32d40c69befdbafd01d19c5ade5fd287a7766f49c3087d907654cb774f59da32349e5c54312b9e3c57667f258f087b6679d643785826b32f173b06546ce930f6ce6ae7f7479edccdb7318f3336de0a7cfdb079545fb2dd80c4af0387b3027ce734243271027f353668eebd50b793d322673d4e5db0b6a8dd8bf85b11352e8527c64666faa59f585aa3bbb7d8b8f0e7bcf2694b5a5cb7add02c8f4b1064d49579273396bd1d7d1f3a7af59d22ffe8bba331e1c1d5018f23255e2f8eb66ff06f2b7ca55e3ad9b9fb46ef3e2cdf651c2ebb4246c8ddcf90419cf822064ff4a0e95dc45be0a0419a3a01629bf21840ab21711431103bcd25035ac5408f5fb8fab30e60a6f50f98f60a04ddfd44106be483e7bda7cdf03084ce94ec371d0982384585ca3f7dee87dac8ab5f7318669c4bd7850cb4e79055ffd247cdaf5352521d1df7b24f9287b9197b48307770d643a25d75fd8f80ae6eaedad7898e36bb0baf227777cbe1f5101e3a187814b1c7b29d6ef87b62e6a554bc3c7da7cf630e92ebf6fdc2676ef0c62376b227d29a2fff7068effd9fe507a53b4e94749d8e1fc4d9eac3e4d2bf638d9948642b7b201a7d0a4b40c5b35410d3a68797799881a3f7d381581627a0b97988dc55e9f379ccd6f295741a241d487c348536c51897993deda93882fa2254a04eab92587d1b3d6df453c624d2cbe89f06327658d557dc182c007fb82bce816647512601b5757a0103b13daf8e4b5fa22760ca1c18dd7ad03f01b5039d2ec1bc5cd6190e989277da2563d960f7023ecc23f5fd54812558c83a062c661740277735477ff5a2a356b0de7dd4ae93da1a6aa5998a80119d26352e1245b1eb082a2cb850aa3d8622e9c91619b14b58a979602339e97e0a591358fee185d5946027c1ff7d570d51b139f0115c9f6b5b83f7ceb5c2095ba70bb03b8082ff53209a440244dbd5c8bb589cf8b6bcb1af28a2b37fe17f320da6e716b20e50c63cf1c4c93bdffebb6463b79b6f3c2770ebd26fb79fbdefbbecfdb3cba9fbfaffad03b2f63c71127dfd150e57806267190a42b8dd7353e2d058704daed556c80e67034061c1ff49abf37c98d00ee354151a647b88f6963caf0194462c8b7befdf82c3841c5e14c683af7cde098e30a5eb6aceb85bf6fd255c43c82c100bf81b4aab18f27314f233085353bb9a2c67bad1b3bf7d840362f77e511e5da2f0ce0d48855d4204da94e9c251f8314be2933ed656c8f7b680da25e47f214b4a506ae49d63b24a5ed20d120a8042f20f00426b21423bbd07ae794c6a6c059326a96a4cb25a257fd6cbfce42f8d702a0ebb8a13dde2289c4e7b29062747f180bcacd4b9158aed887b13836b901b3459f23a6701fc2301e1b89451ef45a22c6ec6edf46034fdc44564f130e7764bb3abfcb470aede6ce6718650569c00c9dd2c67599ef8151c10661e7c0415d55399fc0a599f3ff6b77e442507e3aa4efb1a95b25c99bf40323ad0651f1a059279332c5169b6daa26273d2a69177b238416593b5de178d95d57a15084d3823d9d5eadf7e574933310909fe2768e4572162c83abeae554ebce489c03a3d2f9d5ebef28a47ee820304206a381f0835b40169d5c034b6aa71acb30536362e0161bb2941e2082a015a3f43b5702ce5a50c7944e7c2c11595000163ee9d5640993610575d2118a074f7a961e5244706b2752073647e96c3b91b1d442573ed45e3d83699d44eac26d899eea5f8a03e5104336189aad8228bc1d8ef41d35f9a91609d564ac4ba80cbd6b50d0af171524b3ecdb338c992ec262e291b2a6ef4ecd84efacb1d20380322a9edcefe3535d36dba56a1e1d0f907812e2d03da8302a66f69aaf4d256972ca67a981c3f3d58bdadf1916ab3d8f8ea3b807874cf9d7301102c29f326dbc0f871a3e092e2214035dc45223a2459dd22c83f4341518d8e54e87bcc4456a795b7288d2150fa9bd4012cb53862fcf9243c0d7a8f27754a5a94e98a22758c8996e16b1d6cf3f1715f44c5f1750fb1bac8c39c91c0f090fbcd2c93ab2609ba27e2b5bd325ab210fe462cedcd2b3c1e4ac18bfd4331703a41fe199b081d043d625855eab321289923baf6138f6f3eb306fd0206e7103d4be391d7e41184dad9e751053d58b998d01f2d132c08ba198e087a2ca006afa0ef4f9c03cf94a3056b16a585ad86af476ae44ffd95c2fcf2df8084fe36933ae85bf99b532cfd6ba04cb7608c6c944e36767e3ab012208bc5d5c0885a7528c680bd48a5dfad8883a0a79a027c5a23f08854dcf76d806c622bb24a90450b3d95087aae2375b8d7fe2db593d2302b8dd1d150ea761fb6502ad549d36f8e918ebaa92380f34811d16962d41cbc386d455662f0b5115b904582b476ceab9705181ca944c2f5b2fe12e8e546813435a96be4863432caf8a76e108a2cb8a34bc5ecace3c910eb77ee400c903bbb8e8a1689186fc18ffc07b954f1e1a25e49013097aaa0a8205b2dabd8c242d31a71a13d71c3e9b0aab1eeb3521609db929507cfbdd587cc614736e684bf40628dca7d6870b2830639e179325c5b9fb45cfa75e85c7f43c6945f33d94d3de32e972fedf1d43963081920b9f0cacb02bd3e3362ca6ffa6019808fdd060e1227c1fdbb9020ad21dfe408f36bcc6bc9e15d091fbdfd8e4fd9cf6e29cbae4c6ceae1abb0271cf8d61160666e91f78427bb98a3f39644073e582a8cfec2aefff752c0bad50f8d53fcdf7251a27ab52e7e932ba07affdc310d581a82de9b298033ef844d8bfb9d9c772d78f26236c66999ae9daa2d508eb7b6f9d7b6fb388a35a9d3ca2d00470cd16dd5718c1fa1ea07761568ce6cd5721c8b6b71e8fd8a3147026d1c1b7d66eb3e868c3f7df23e13399fa25d7d32646398f4e949254b093d9551eb76abc8f90c1e4d5a1a9ff5619f8f2171e1a92c6149aaded5516491cf0650e6982c1b7fa4ac23b40609d79616afb20e26b09f9cc72cd7e99ef4265698ed9f2b8db3edaca67036d0ea4a5737d56ea57dcef6ee7409eede85b570a2d6f354d538abb03dda94319896d7e9616e14717d7cc1e7a647fba10dc907baa0b68c5c0dbb6a19e3eca4658bbcc76a13af489260e667a7fdb109cd561ab3d95d3d6a064dcf2f6d275bcb79261f37a348d40e928c5b699bad52b61d32bbea1b3b7c7863cb1f7d3fe96ca30101d655db55640cf61c807ae55d1b278d0f97e488b444ad30d78a66be16c5d75ce235dc0252615a77a975acff34f7df175b1bb7d0ec4bae36cc7ab931f9d257b97ef972ab2e75ab646aac2d2623e5956fa87e061f5382eb1058876b8f49a5c3e45bab158a085ba37ab1b1f684992ed3b5d60045854c8ee493551b5d9ed712c98e2551f7af16eb90c58edce910ae4ad133cd8c7a2c6c9486696e9d5a1cb184bec715eafd73d42140a2a8b1ae01aaf491e6468e649e1c9ddddc40ed14d693b4573b55560328a60719edbc4e475a5c440cd7bc1c334a9f2a34893c670687d12555692913784b7be64ac2f52b7c9d46b6e92190a07eddd496328bad25a6821b693fc5d37a38f4d8982a1c7ca9081ba5fdb82017850c66091b02bc99ab0aa86cad2f40290cab3d5bab3121378ea26964381940e262a5a38941a65f7d07322beb6c4601755fe301635440a8df73acdbf62d6123dfceaa288f148f7568b823acebf7b9ccfbc345d2d6583a2bef4ed114e7cfb0b62b6692312012784fc1d5cc1395a49ccee79fa139d5001e32539ee2028d2be43db627fbd17e2e598ea83f2235d594777241e3a925eed08c4f4e9dbd7bc57b7608fafd1e3922a587114d7feb2ac496ce87403a909e419b4ac06e27484d5a02cab964499b2fb914474818ecf356e8c57011232bb7479a14e4752812a62a5ca9d12ccc41e21a9b99651ed00ceb6aa021682676b50a465019ecd124fe6e50a821324a6f8272cd853091393c34a83f4fbea07916b98a3d9e07aa98c6d540da4eab91741df7875522611d00fa3cb02db9693591a0599537ac1c94986c8b99e151c302f9d2d169ebbff25a8007792011f4e3f251656615798998cab1a52bd1224bc9a86132bcc2885cf91eb2d1b856d8bc1102234569de87658d717f8eab3860e6303967993a4cb06899d0a227343f547333cd264c85a8ff7062ef9d7eb2226a3d3cdd9ed61eb4484668ddb9469da24355613480e069bd212d81d66d4348fda18dd1f8d98e79b06ca47ae38768a10f3840ec3537d86ab52cb4f6ab61180761fc802d8d78403ea8b8449c90631c5b2a41b3ad5b564201f9581de8ee187b498357f28d476b888f3ce1b36fa76f91104f465655298528e1827de5f3bd2f58f197bb44181fa276b959d40bebee04c4db94b1a91d249e827529b30b1033762ddfea348017b9d782564241a295cc5a99f7187acf5d3d7d2f96c4d300d2261277c29d055cf88539b3f4e241f012c9272af8b9a88224019846b86d8e009eec1b802db2df4d13fa5e5b885eec65d09f8bcb2ab3542be00371eac98e3f74b156c4278949de2a9225486a317381d4a6457f45aaccb5e7bbda5560b5b0f5d53cee7c50570f9a61a3ac4a15921394e2b9bdb6e5d1290f4622a275d85941b2526077d52542a4a162a4e76cc33defe514c23251b365c1ddfa0619ed362019d2908540dae7959f6a66752dd0ca3681001c017613a4b63a65c83ba60d271c9da2b312b052f6bad858b954922ce904b9acf061b5b891c126d78ae796bc3ad794fb59ca45cda5c4dfe43a2949c298db3f0e4cb582cac1a696982672768b7357606d4ba79fd23a019c64fd23aec447353513eede11baf872ad5ebee7cd4fff740709c6ac3a52ed312f7ba6feadc4627bdf8ffbe1fa4f7ce2e3a7d6c79ccffd3096f81df43de499f4ff9893ee45fc3898f2eec38d1ff22f878937f3906247a421b9564e61bef8e9817b8ebb86349b340be7b78b3df53a99d444aa14b0ac52834f965571a4c1f216397f8f1ca62e16e74f5a1a9c4bebea4e8d63f9aca216703257c9ec1a06446130da510306472d002ba4fe984aca626c9cc79c47a5f993a43648bb601a5d62ca7b8041477ba1dcfcc85221837be644d8908ffab661b852cff43a893181c2c61f6ccfd1bb687c7ae0e074a94d457867c73fb538658b4d3f2c945673e9fd0c4f0fcadc61c07096762c4fa872408eea3a3f571e58c33fa40dd9d4002b4007bd607482e2ea4d0065a7ec866ca4b55f89fb6afd553cc4fcfeb0f208e634efbafac1d0f5c5a2d8017747243fb742d9054aa784b2a5406ec46d50361706d7411957a58278d133471f303c1556d9c647d36833edb78997660b9fe51a464ed985bd1d626b92dd1d9b04650598e16749abe964000fe5ca4df3720fd159cf6c7ba414f4c30a658e080733091fd080254fe42c864873083da542b3630d2cc953520b4b80f0a9efb4842dfff78878f2cf669f48f5c400d7944eefceb596cf2c88f3fa3264f0afc39b2a23979e79bce397d73767567e594ee5a8b000f9ee50b9e7c9410d53c7ce5cd3f1be23b7e1dab5e2dc6c31aebec72c5cbaf2d7e9855995a545997db06a10f6fc57b4237964f73d07f69517be5970726a6ceb7ccdcc99926fe33589074e2d6817a16e6edde19818e936e9f844e3ea812bde36a6161c3aedba6da174f073b6b4dde48fe8c2719e7897706fab222fdcee8def2e1cda3a499801684d8785239af82cb745292b66249cd652e1ffb32343dd43d74523c5e941817dcf855e5cf6c9281d64981bd6747be393adc1a58559d443443741979f888118b2c9ad913ac328a30ff5b15172dd5ac8c866ca87874cd56e41474aa9d4b1e5cae0151efebdb133db22c67af899631b969fd179f82963e78483c523791114c764ce5ff55ffba1be90228ffdd4faa42e0dfbc1667ede717f7da43d010dc298bb6247b15fa2ea8d11320a4b04590191d5d54fea65fea87f70c708c60475ebaf02720091a6ab0735c863ab90dc8e4ed2fea80ba24c888c9d4e90f6e168f1529dddb4026722e503507c40bcc86f0a1fca74778935e8a0dcc73034ee828cc746a8d6c4983445cbee0a08fd8dffcb3b097a45874894c119adf79d1c854aee7de0f6b899f47b8ee0ef3a275cf07d8b18dc05af5b59e52304dfc69f847eb8529a05c8c26d7380109a48b7a32d80ec6706f49b9f982aacfceb8f9f85da7e89ae6d1df5cbdbc3b83f5bfbf7f1cf7090309b042532fb7ecc8516135be51061e0e75eddbe0231bbd31c554c2ed9d9650c19697deea9ca76387170e5f83f8d2cd52ec10620738ee02ed9ec4ac578bc65709b38adeeedb64c2fe6e95416108f6634e8a1059413fb8919be08da6c223a7ec2680b6d6be66d7172f63853dcd2cb039141b68ed0d871524638619edbebe9d1d929b36e5f0c07892a93a0d6669c6b7b31abc3e02e7992f5a3303c277ee62e1dd8add86117f94e2232b091be9149312d28bf208cb9979629927e32905ba16a47608f49fda447750f9fda023f7c5644d07b32dffa52516ed5092aaf7e740b9776073e53968ddc7caac8ffd3425df83805bf451d0f50f553bc7047387e4e35d2c4af8bd355f8cbe2059952e0d06b22ff062b62da6ef51bacc2c6ca3eaf823397ccd00472690ec2ab345746095912c4709a9ce5ab603fd17a919351e5e6e9ba77783115ad4dc9f6e8ec180327561d9f6c29bc3ada58299d39fff2fc1bfd7a26cddc32edbdcf73447ee04bae44933725f3324691f141d7766cc9d9b8c7aa852732d888b29dd6b70544951f7d90ce4275091ace05fa65155218c89033e4bbff6c7b5611f693fda520cbe3bba45f3ad430d7f94cf86ccac159ef3624873ef215ae12adaa6b303dffaddfbc417047c2a903bd474fc80debf8eae8f72ba6a7e1f1e93b4286edaaea18d8c8e67bf4945f76648d70f33e52b3399cd7b9fb26f1c159d92d6d77470da5e9d915db0c13770d324cdae5b564c37bcdf3e6edeed66dca73ead503a4019a51ebb1c9bd74129fd66324d8ea31ee49eaac5c32832300188e1fa0c7b890daaf4e2003da2c406e4db5f7331af14e31052753219b1762b2acff7c99b89506a1263c403424cea96cb7ca597d86f926a35bc065da69b3baca8c5babed0cde75b65a8d65361064f29f974d11e931b68e327477b337a63436b517e69eec3c287257c3e954f9c4b3ff24ae96379d568e49eab637e32848a447d0cc84cf5f7e9b16cdb6d8013640566431ec9d7e2343fe2ea275c1fdddcdf25edfb5ed87ed2d6c5b3d75304c977c3b6a74648a8976d22a112d427a8c2da3747a8c7e0d19bdb50cdb6223f2069375b1951b0277fd5881977448cddcfee29dffe4eb2b2526cf31906f33cccc1a36a577b4e4e05f03eddbbe7cadbe12fbfa57e76c45ef0d5ce4b12549a7a6cc329b3536463587318babd5e2d966238bab1b39911ea34d94522c415a1574cd468c1e13942bd067446615d27aeaadc96557a29fbacf1a97dbe1daf6fbd555a3b203600e44dfa599f593e83186a85a358830a90dff9d41475e543a2972e5d396dfa11fd638c46c55b6c68cab73dca4f33274d274709aa84df5aaf4189b1ab619c959588b31e30b52482c6ea6149e5620f05a564351c39e5a85154fcd91c5716512fd10628e46da00589563644be23725c5045dc95592880dcd597faa80c1ab50a94856e4f969296a4a3e541171cf2d7860f09d3b8b43e68dba371978ad15895abb8eac13c4041dcd916562914fc79b1f6bea36eed849d4e39c2b2e229f7583a24fdc63f56e8c6609a239964917060d949f4c8b8722e212f0acb23a1fb166df5bdbd17b3af659dfac56bb5e16267c0bb2ce923b6e3fab51f064042315b15ea77d6e4011f15d19ed9dc42f7bb804b28e29006a58c28985fa40bb9762d736a3da611e29e7534a2094d69b9055e8864a73bd08d90b4f04962be8bb0ee1b3c5f46f5ac091220163f76ef864e6482e96516258a51ff7d2d3afde59694cbefc87fee01d0e890f856c857fa59df8a06a3247ee9e7d0192fdae23d93f9598400ed1d2b2a97231cb417f2292d502242847b24740b6801f23b58fc1a0e8523a0b28502aee4a6ef833705537aa3fc83755fe65820984f1d4c582de9875e20c9738ca691b13517f7158625154d1bb36a45948c7219df82a848f47f2a05f927a356c0de5267a391119143f3b4d740b4ba64816822227f379099d3c55fd8748d6718ee822bde469227dc8decb30adcfb2f8e28c61b7cb7a6d2953873ca831654b4e1ec06f8aaa73730e56b104d1f7f5670831eb17c7b152e6b0b167d2e8587ced593ddf429e9cc085fb25fbf252f02ff34b590933be28583a5504f98994027dd629502ba9b5bdb3bffc5f505bb60a34b83b2e6545e9e429ff928ebb2e437e89fc071a83559ca575a8b5b229e95f1e023930024ac7804f043042385dcb50abffa6a59984bce63009f94fd3f987fc8da4e1a453e77a798be13f85fad0b10a96b02abb30978f294e8c98d50ae8e44dae7e0ec08c9329ad1f2814cca17450c0504a25a983029b050a1aa0cb28074fd9eca9ca7e990043c114dbb95594265b0f4da68b924a887d2d0e62991b700a10c116d0670ca471653a663a413153f7419716bb6d5db48431e38b9b4c5e128b731de2e38ca62764e59c636232a15011f73701b0f48a8cffc00faa54c2a2cf84c231c9844255b2a04b0b5afd22e8f2912cb54e0685650978233f6c06c8e92eceaad0ac320ec1063ad6e44723db997245ae53c30ab692f99b845a754606677195d98c83beb8dac6e036934d098536f28fdb5794735a94c7995549f8d12f7a7a8fcb5774e5d5c7081ada648256cf4fbd9903450bcb1f83e1bbff4c9c5eb6d990bc7c25ed1fa83e6c9e32833c97be71eca09edde71f59b4e7c534df0d33ae7c6afaf53e656978d69144715cdf2b49b35e73d8765900f6c639488ec03f55ff7e8c99372176a8bdf69f8ba7db4fe256159eded298d4dc2629e00ff971dd31e66683ea40e330f4001bbcf2a4efe9d7765da6a8edc9cd756627d004bd7ee9caf2f19d3b3f77a0bad98bffb8221e33dfee1b5ab3e6e3f1d61d6d8b17ffea53fdf5f37c43c02a7a67df55ef478ddce6cb517f4361bcdc34c5dc83b8a36c5d45f41ebfdb5bd7df253b9c7f1caa6f11283fc466d5bfac9aa01d6b73f9d8e2187de78d73d4ebab07beecfdafdbe0f137625e3435cff390059d169dfe5ca379a9dabc73eeb9dada4c705a43ebecbbfd2ba397b5dd5d2e2a48a378fc9b7ae4e5d9533b857bfef04edba50c8df4f5611774d598378c146f135f8c6f9919e708fd5e7fc29bf50fdaa04b785678c97cf35e979f01210f872c3e19fd34998929f737f2595e9c51c0f2623f316336bcafe4ddaa37ae4d5911f9e286b4276b2f667cfd78aca8efe68d803b366eed792ff98d4f04dce23de4b6cf4cd513c2f59a4eeac23d9b32a135b4eeeb6f87a27c69e7bf177fabef32d5fdef4311dfcc2f2775ba6081af0b99f6ea995bd7950c02"; + + function setUp() public { + vm.createSelectFork("https://rpc.ankr.com/eth_sepolia"); + espressoTEEVerifier = new EspressoTEEVerifier(mrEnclave, mrSigner, v3QuoteVerifier); + + string memory quotePath = "/test/foundry/configs/sequencer_inbox_attestation.bin"; + string memory inputFile = string.concat(vm.projectRoot(), quotePath); + sampleQuote = vm.readFileBinary(inputFile); + seqInboxImpl = new SequencerInbox(maxDataSize, IReader4844(reader4844), false); + rollupMock = new RollupMock(rollupOwner); + bridgeImpl = new Bridge(); + bridge = Bridge( + address(new TransparentUpgradeableProxy(address(bridgeImpl), proxyAdmin, "")) + ); + + bridge.initialize(IOwnable(address(rollupMock))); + vm.prank(rollupOwner); + bridge.setDelayedInbox(dummyInbox, true); + + seqInboxImpl = new SequencerInbox(maxDataSize, IReader4844(reader4844), false); + seqInbox = SequencerInbox( + address(new TransparentUpgradeableProxy(address(seqInboxImpl), proxyAdmin, "")) + ); + seqInbox.initialize(bridge, maxTimeVariation, address(espressoTEEVerifier)); + + vm.prank(rollupOwner); + seqInbox.setIsBatchPoster(tx.origin, true); + + vm.prank(rollupOwner); + bridge.setSequencerInbox(address(seqInbox)); + } + + function testAddSequencerL2BatchFromOrigin() public { + uint256 subMessageCount = 1; + uint256 nextSubMessageCount = 18; + uint256 sequenceNumber = 1; + uint256 delayedMessagesRead = 10; + bytes32 reportDataHash = keccak256( + abi.encode( + sequenceNumber, + delayedMessagesRead, + l2TEEData, + address(0), + subMessageCount, + nextSubMessageCount + ) + ); + + vm.prank(tx.origin); + vm.expectRevert(); + + // We expect the TEE attestation quote to be validated + vm.expectEmit(); + emit TEEAttestationQuoteVerified(sequenceNumber); + seqInbox.addSequencerL2BatchFromOrigin( + sequenceNumber, + l2TEEData, + delayedMessagesRead, + IGasRefunder(0x0000000000000000000000000000000000000000), + subMessageCount, + nextSubMessageCount, + sampleQuote + ); + } + + function testAddSequencerL2BatchFromOriginWithIncorrectParams() public { + bytes memory invalidData = hex"012346"; + uint256 subMessageCount = 1; + uint256 nextSubMessageCount = 18; + uint256 sequenceNumber = 1; + uint256 delayedMessagesRead = 10; + + vm.prank(tx.origin); + vm.expectRevert(abi.encodeWithSelector(InvalidReportDataHash.selector)); + seqInbox.addSequencerL2Batch( + sequenceNumber, + invalidData, + delayedMessagesRead, + IGasRefunder(0x0000000000000000000000000000000000000000), + subMessageCount, + nextSubMessageCount, + sampleQuote + ); + } + + function testAddSequencerL2Batch() public { + uint256 subMessageCount = 1; + uint256 nextSubMessageCount = 18; + uint256 sequenceNumber = 1; + uint256 delayedMessagesRead = 10; + bytes32 reportDataHash = keccak256( + abi.encode( + sequenceNumber, + delayedMessagesRead, + l2TEEData, + address(0), + subMessageCount, + nextSubMessageCount + ) + ); + + vm.prank(tx.origin); + vm.expectRevert(); + + // We expect the TEE attestation quote to be validated + vm.expectEmit(); + emit TEEAttestationQuoteVerified(sequenceNumber); + seqInbox.addSequencerL2Batch( + sequenceNumber, + l2TEEData, + delayedMessagesRead, + IGasRefunder(0x0000000000000000000000000000000000000000), + subMessageCount, + nextSubMessageCount, + sampleQuote + ); + } + + function testAddSequencerL2BatchWithIncorrectParams() public { + bytes memory invalidData = hex"012346"; + uint256 subMessageCount = 1; + uint256 nextSubMessageCount = 18; + uint256 sequenceNumber = 1; + uint256 delayedMessagesRead = 10; + + vm.prank(tx.origin); + vm.expectRevert(abi.encodeWithSelector(InvalidReportDataHash.selector)); + seqInbox.addSequencerL2Batch( + sequenceNumber, + invalidData, + delayedMessagesRead, + IGasRefunder(0x0000000000000000000000000000000000000000), + subMessageCount, + nextSubMessageCount, + sampleQuote + ); + } +} diff --git a/test/foundry/configs/attestation.bin b/test/foundry/configs/attestation.bin new file mode 100644 index 000000000..d4664cfae Binary files /dev/null and b/test/foundry/configs/attestation.bin differ diff --git a/test/foundry/configs/incorrect_header_in_quote.bin b/test/foundry/configs/incorrect_header_in_quote.bin new file mode 100644 index 000000000..6a16778cb Binary files /dev/null and b/test/foundry/configs/incorrect_header_in_quote.bin differ diff --git a/test/foundry/configs/invalid_quote.bin b/test/foundry/configs/invalid_quote.bin new file mode 100644 index 000000000..28378ce88 Binary files /dev/null and b/test/foundry/configs/invalid_quote.bin differ diff --git a/test/foundry/configs/sequencer_inbox_attestation.bin b/test/foundry/configs/sequencer_inbox_attestation.bin new file mode 100644 index 000000000..7b1449b2f Binary files /dev/null and b/test/foundry/configs/sequencer_inbox_attestation.bin differ diff --git a/test/foundry/configs/tcbinfo.json b/test/foundry/configs/tcbinfo.json new file mode 100644 index 000000000..760817ca8 --- /dev/null +++ b/test/foundry/configs/tcbinfo.json @@ -0,0 +1 @@ +{"tcbInfo":{"version":2,"issueDate":"2024-06-13T10:29:20Z","nextUpdate":"2024-07-13T10:29:20Z","fmspc":"00606a000000","pceId":"0000","tcbType":0,"tcbEvaluationDataNumber":16,"tcbLevels":[{"tcb":{"sgxtcbcomp01svn":12,"sgxtcbcomp02svn":12,"sgxtcbcomp03svn":3,"sgxtcbcomp04svn":3,"sgxtcbcomp05svn":255,"sgxtcbcomp06svn":255,"sgxtcbcomp07svn":1,"sgxtcbcomp08svn":0,"sgxtcbcomp09svn":0,"sgxtcbcomp10svn":0,"sgxtcbcomp11svn":0,"sgxtcbcomp12svn":0,"sgxtcbcomp13svn":0,"sgxtcbcomp14svn":0,"sgxtcbcomp15svn":0,"sgxtcbcomp16svn":0,"pcesvn":13},"tcbDate":"2023-08-09T00:00:00Z","tcbStatus":"SWHardeningNeeded"},{"tcb":{"sgxtcbcomp01svn":12,"sgxtcbcomp02svn":12,"sgxtcbcomp03svn":3,"sgxtcbcomp04svn":3,"sgxtcbcomp05svn":255,"sgxtcbcomp06svn":255,"sgxtcbcomp07svn":0,"sgxtcbcomp08svn":0,"sgxtcbcomp09svn":0,"sgxtcbcomp10svn":0,"sgxtcbcomp11svn":0,"sgxtcbcomp12svn":0,"sgxtcbcomp13svn":0,"sgxtcbcomp14svn":0,"sgxtcbcomp15svn":0,"sgxtcbcomp16svn":0,"pcesvn":13},"tcbDate":"2023-08-09T00:00:00Z","tcbStatus":"ConfigurationAndSWHardeningNeeded"},{"tcb":{"sgxtcbcomp01svn":11,"sgxtcbcomp02svn":11,"sgxtcbcomp03svn":3,"sgxtcbcomp04svn":3,"sgxtcbcomp05svn":255,"sgxtcbcomp06svn":255,"sgxtcbcomp07svn":1,"sgxtcbcomp08svn":0,"sgxtcbcomp09svn":0,"sgxtcbcomp10svn":0,"sgxtcbcomp11svn":0,"sgxtcbcomp12svn":0,"sgxtcbcomp13svn":0,"sgxtcbcomp14svn":0,"sgxtcbcomp15svn":0,"sgxtcbcomp16svn":0,"pcesvn":13},"tcbDate":"2023-02-15T00:00:00Z","tcbStatus":"OutOfDate"},{"tcb":{"sgxtcbcomp01svn":11,"sgxtcbcomp02svn":11,"sgxtcbcomp03svn":3,"sgxtcbcomp04svn":3,"sgxtcbcomp05svn":255,"sgxtcbcomp06svn":255,"sgxtcbcomp07svn":0,"sgxtcbcomp08svn":0,"sgxtcbcomp09svn":0,"sgxtcbcomp10svn":0,"sgxtcbcomp11svn":0,"sgxtcbcomp12svn":0,"sgxtcbcomp13svn":0,"sgxtcbcomp14svn":0,"sgxtcbcomp15svn":0,"sgxtcbcomp16svn":0,"pcesvn":13},"tcbDate":"2023-02-15T00:00:00Z","tcbStatus":"OutOfDateConfigurationNeeded"},{"tcb":{"sgxtcbcomp01svn":7,"sgxtcbcomp02svn":9,"sgxtcbcomp03svn":3,"sgxtcbcomp04svn":3,"sgxtcbcomp05svn":255,"sgxtcbcomp06svn":255,"sgxtcbcomp07svn":1,"sgxtcbcomp08svn":0,"sgxtcbcomp09svn":0,"sgxtcbcomp10svn":0,"sgxtcbcomp11svn":0,"sgxtcbcomp12svn":0,"sgxtcbcomp13svn":0,"sgxtcbcomp14svn":0,"sgxtcbcomp15svn":0,"sgxtcbcomp16svn":0,"pcesvn":13},"tcbDate":"2022-08-10T00:00:00Z","tcbStatus":"OutOfDate"},{"tcb":{"sgxtcbcomp01svn":7,"sgxtcbcomp02svn":9,"sgxtcbcomp03svn":3,"sgxtcbcomp04svn":3,"sgxtcbcomp05svn":255,"sgxtcbcomp06svn":255,"sgxtcbcomp07svn":0,"sgxtcbcomp08svn":0,"sgxtcbcomp09svn":0,"sgxtcbcomp10svn":0,"sgxtcbcomp11svn":0,"sgxtcbcomp12svn":0,"sgxtcbcomp13svn":0,"sgxtcbcomp14svn":0,"sgxtcbcomp15svn":0,"sgxtcbcomp16svn":0,"pcesvn":13},"tcbDate":"2022-08-10T00:00:00Z","tcbStatus":"OutOfDateConfigurationNeeded"},{"tcb":{"sgxtcbcomp01svn":4,"sgxtcbcomp02svn":4,"sgxtcbcomp03svn":3,"sgxtcbcomp04svn":3,"sgxtcbcomp05svn":255,"sgxtcbcomp06svn":255,"sgxtcbcomp07svn":0,"sgxtcbcomp08svn":0,"sgxtcbcomp09svn":0,"sgxtcbcomp10svn":0,"sgxtcbcomp11svn":0,"sgxtcbcomp12svn":0,"sgxtcbcomp13svn":0,"sgxtcbcomp14svn":0,"sgxtcbcomp15svn":0,"sgxtcbcomp16svn":0,"pcesvn":11},"tcbDate":"2021-11-10T00:00:00Z","tcbStatus":"OutOfDate"},{"tcb":{"sgxtcbcomp01svn":4,"sgxtcbcomp02svn":4,"sgxtcbcomp03svn":3,"sgxtcbcomp04svn":3,"sgxtcbcomp05svn":255,"sgxtcbcomp06svn":255,"sgxtcbcomp07svn":0,"sgxtcbcomp08svn":0,"sgxtcbcomp09svn":0,"sgxtcbcomp10svn":0,"sgxtcbcomp11svn":0,"sgxtcbcomp12svn":0,"sgxtcbcomp13svn":0,"sgxtcbcomp14svn":0,"sgxtcbcomp15svn":0,"sgxtcbcomp16svn":0,"pcesvn":10},"tcbDate":"2020-11-11T00:00:00Z","tcbStatus":"OutOfDate"},{"tcb":{"sgxtcbcomp01svn":4,"sgxtcbcomp02svn":4,"sgxtcbcomp03svn":3,"sgxtcbcomp04svn":3,"sgxtcbcomp05svn":255,"sgxtcbcomp06svn":255,"sgxtcbcomp07svn":0,"sgxtcbcomp08svn":0,"sgxtcbcomp09svn":0,"sgxtcbcomp10svn":0,"sgxtcbcomp11svn":0,"sgxtcbcomp12svn":0,"sgxtcbcomp13svn":0,"sgxtcbcomp14svn":0,"sgxtcbcomp15svn":0,"sgxtcbcomp16svn":0,"pcesvn":5},"tcbDate":"2018-01-04T00:00:00Z","tcbStatus":"OutOfDate"}]},"signature":"5f86b5de1eb07f3888f2da99ed654a67d2983e8dd219fd48b74a302847e4f96514cf66d312288c7535901279c5230d18f7181b5e40c3ebc5e38a30e38d98d642"} \ No newline at end of file diff --git a/test/foundry/configs/tee_identity.json b/test/foundry/configs/tee_identity.json new file mode 100644 index 000000000..dc22dba42 --- /dev/null +++ b/test/foundry/configs/tee_identity.json @@ -0,0 +1 @@ +{"enclaveIdentity":{"id":"QE","version":2,"issueDate":"2024-06-13T10:21:07Z","nextUpdate":"2024-07-13T10:21:07Z","tcbEvaluationDataNumber":16,"miscselect":"00000000","miscselectMask":"FFFFFFFF","attributes":"11000000000000000000000000000000","attributesMask":"FBFFFFFFFFFFFFFF0000000000000000","mrsigner":"8C4F5775D796503E96137F77C68A829A0056AC8DED70140B081B094490C57BFF","isvprodid":1,"tcbLevels":[{"tcb":{"isvsvn":8},"tcbDate":"2023-08-09T00:00:00Z","tcbStatus":"UpToDate"},{"tcb":{"isvsvn":6},"tcbDate":"2021-11-10T00:00:00Z","tcbStatus":"OutOfDate"},{"tcb":{"isvsvn":5},"tcbDate":"2020-11-11T00:00:00Z","tcbStatus":"OutOfDate"},{"tcb":{"isvsvn":4},"tcbDate":"2019-11-13T00:00:00Z","tcbStatus":"OutOfDate"},{"tcb":{"isvsvn":2},"tcbDate":"2019-05-15T00:00:00Z","tcbStatus":"OutOfDate"},{"tcb":{"isvsvn":1},"tcbDate":"2018-08-15T00:00:00Z","tcbStatus":"OutOfDate"}]},"signature":"8b00cbe1be4e7c9cca76a9c82cca19ad513fbd3cbb78356f6596c6cd50b6991287cad59d308052351bce8067ff8c8c99f92a599de19e7afdcc3d87048157e81e"} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 06a9ee890..fbf7a39ec 100644 --- a/yarn.lock +++ b/yarn.lock @@ -802,17 +802,24 @@ "@nomicfoundation/ethereumjs-rlp" "5.0.4" ethereum-cryptography "0.1.3" -"@nomicfoundation/hardhat-verify@^2.0.9": - version "2.0.9" - resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-verify/-/hardhat-verify-2.0.9.tgz#98a1c9a3742b008be71a709d074f10dec23bc5f0" - integrity sha512-7kD8hu1+zlnX87gC+UN4S0HTKBnIsDfXZ/pproq1gYsK94hgCk+exvzXbwR0X2giiY/RZPkqY9oKRi0Uev91hQ== +"@nomicfoundation/hardhat-foundry@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-foundry/-/hardhat-foundry-1.1.2.tgz#4f5aaa1803b8f5d974dcbc361beb72d49c815562" + integrity sha512-f5Vhj3m2qvKGpr6NAINYwNgILDsai8dVCsFb1rAVLkJxOmD2pAtfCmOH5SBVr9yUI5B1z9rbTwPBJVrqnb+PXQ== + dependencies: + chalk "^2.4.2" + +"@nomicfoundation/hardhat-verify@^2.0.12": + version "2.0.12" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-verify/-/hardhat-verify-2.0.12.tgz#480819a245a2db0b127e473c62079f7b4f16daa8" + integrity sha512-Lg3Nu7DCXASQRVI/YysjuAX2z8jwOCbS0w5tz2HalWGSTZThqA0v9N0v0psHbKNqzPJa8bNOeapIVSziyJTnAg== dependencies: "@ethersproject/abi" "^5.1.2" "@ethersproject/address" "^5.0.2" cbor "^8.1.0" - chalk "^2.4.2" debug "^4.1.1" lodash.clonedeep "^4.5.0" + picocolors "^1.1.0" semver "^6.3.0" table "^6.8.0" undici "^5.14.0" @@ -892,26 +899,26 @@ "@openzeppelin/contracts" "4.7.3" "@openzeppelin/contracts-upgradeable" "4.7.3" -"@openzeppelin/contracts-upgradeable@4.5.2": - version "4.5.2" - resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.5.2.tgz#90d9e47bacfd8693bfad0ac8a394645575528d05" - integrity sha512-xgWZYaPlrEOQo3cBj97Ufiuv79SPd8Brh4GcFYhPgb6WvAq4ppz8dWKL6h+jLAK01rUqMRp/TS9AdXgAeNvCLA== - "@openzeppelin/contracts-upgradeable@4.7.3": version "4.7.3" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.7.3.tgz#f1d606e2827d409053f3e908ba4eb8adb1dd6995" integrity sha512-+wuegAMaLcZnLCJIvrVUDzA9z/Wp93f0Dla/4jJvIhijRrPabjQbZe6fWiECLaJyfn5ci9fqf9vTw3xpQOad2A== -"@openzeppelin/contracts@4.5.0": - version "4.5.0" - resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.5.0.tgz#3fd75d57de172b3743cdfc1206883f56430409cc" - integrity sha512-fdkzKPYMjrRiPK6K4y64e6GzULR7R7RwxSigHS8DDp7aWDeoReqsQI+cxHV1UuhAqX69L1lAaWDxenfP+xiqzA== +"@openzeppelin/contracts-upgradeable@4.8.0": + version "4.8.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.8.0.tgz#26688982f46969018e3ed3199e72a07c8d114275" + integrity sha512-5GeFgqMiDlqGT8EdORadp1ntGF0qzWZLmEY7Wbp/yVhN7/B3NNzCxujuI77ktlyG81N3CUZP8cZe3ZAQ/cW10w== "@openzeppelin/contracts@4.7.3": version "4.7.3" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.7.3.tgz#939534757a81f8d69cc854c7692805684ff3111e" integrity sha512-dGRS0agJzu8ybo44pCIf3xBaPQN/65AIXNgK8+4gzKd5kbvlqyxryUYVLJv7fK98Seyd2hDZzVEHSWAh0Bt1Yw== +"@openzeppelin/contracts@4.8.0": + version "4.8.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.8.0.tgz#6854c37df205dd2c056bdfa1b853f5d732109109" + integrity sha512-AGuwhRRL+NaKx73WKRNzeCxOCOCxpaqF+kp8TJ89QzAipSwZy/NoflkWaL9bywXFRhIzXt8j38sfF7KBKCPWLw== + "@resolver-engine/core@^0.3.3": version "0.3.3" resolved "https://registry.yarnpkg.com/@resolver-engine/core/-/core-0.3.3.tgz#590f77d85d45bc7ecc4e06c654f41345db6ca967" @@ -1543,14 +1550,14 @@ ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.6.1, ajv@^6.9.1: uri-js "^4.2.2" ajv@^8.0.1: - version "8.16.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.16.0.tgz#22e2a92b94f005f7e0f9c9d39652ef0b8f6f0cb4" - integrity sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw== + version "8.17.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== dependencies: fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" json-schema-traverse "^1.0.0" require-from-string "^2.0.2" - uri-js "^4.4.1" amdefine@>=0.0.4: version "1.0.1" @@ -3413,6 +3420,11 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== +fast-uri@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.3.tgz#892a1c91802d5d7860de728f18608a0573142241" + integrity sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw== + fastq@^1.6.0: version "1.17.1" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" @@ -5478,6 +5490,11 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== +picocolors@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" @@ -6974,7 +6991,7 @@ unpipe@1.0.0: resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== -uri-js@^4.2.2, uri-js@^4.4.1: +uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==