From e080cd28892ac536c70929860e309c6352d3a6d7 Mon Sep 17 00:00:00 2001 From: Patrick McCorry Date: Mon, 8 Mar 2021 12:05:07 +0000 Subject: [PATCH 1/2] First draft of changes --- .../messaging/OVM_L1CrossDomainMessenger.sol | 3 +- .../chain/OVM_CanonicalTransactionChain.sol | 51 +- .../OVM/chain/OVM_StateCommitmentChain.sol | 43 +- .../OVM/verification/OVM_FraudVerifier.sol | 2 +- .../chain/iOVM_CanonicalTransactionChain.sol | 12 +- .../iOVM/chain/iOVM_StateCommitmentChain.sol | 12 +- .../libraries/standards/ReplayProtection.sol | 29 + hardhat.config.ts | 24 +- .../base/OVM_L1CrossDomainMessenger.spec.ts | 285 ++-- .../OVM_CanonicalTransactionChain.spec.ts | 1258 +++++++++-------- .../chain/OVM_StateCommitmentChain.spec.ts | 478 ++++--- .../verification/OVM_FraudVerifier.spec.ts | 374 ++--- 12 files changed, 1402 insertions(+), 1169 deletions(-) create mode 100644 contracts/optimistic-ethereum/libraries/standards/ReplayProtection.sol diff --git a/contracts/optimistic-ethereum/OVM/bridge/messaging/OVM_L1CrossDomainMessenger.sol b/contracts/optimistic-ethereum/OVM/bridge/messaging/OVM_L1CrossDomainMessenger.sol index 7cb89b3c2..72b439ddc 100644 --- a/contracts/optimistic-ethereum/OVM/bridge/messaging/OVM_L1CrossDomainMessenger.sol +++ b/contracts/optimistic-ethereum/OVM/bridge/messaging/OVM_L1CrossDomainMessenger.sol @@ -287,7 +287,8 @@ contract OVM_L1CrossDomainMessenger is iOVM_L1CrossDomainMessenger, Abs_BaseCros iOVM_CanonicalTransactionChain(resolve("OVM_CanonicalTransactionChain")).enqueue( resolve("OVM_L2CrossDomainMessenger"), _gasLimit, - _message + _message, + 0, "" ); } } diff --git a/contracts/optimistic-ethereum/OVM/chain/OVM_CanonicalTransactionChain.sol b/contracts/optimistic-ethereum/OVM/chain/OVM_CanonicalTransactionChain.sol index d1ea920dd..8bef81442 100644 --- a/contracts/optimistic-ethereum/OVM/chain/OVM_CanonicalTransactionChain.sol +++ b/contracts/optimistic-ethereum/OVM/chain/OVM_CanonicalTransactionChain.sol @@ -8,6 +8,7 @@ import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol"; import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol"; import { Lib_MerkleTree } from "../../libraries/utils/Lib_MerkleTree.sol"; import { Lib_Math } from "../../libraries/utils/Lib_Math.sol"; +import { ReplayProtection } from "../../libraries/standards/ReplayProtection.sol"; /* Interface Imports */ import { iOVM_CanonicalTransactionChain } from "../../iOVM/chain/iOVM_CanonicalTransactionChain.sol"; @@ -30,7 +31,7 @@ import { OVM_ExecutionManager } from "../execution/OVM_ExecutionManager.sol"; * Compiler used: solc * Runtime target: EVM */ -contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_AddressResolver { +contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_AddressResolver, ReplayProtection { /************* * Constants * @@ -263,16 +264,21 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad return uint40(queue().length() / 2); } + /** * Adds a transaction to the queue. * @param _target Target L2 contract to send the transaction to. * @param _gasLimit Gas limit for the enqueued L2 transaction. * @param _data Transaction data. + * @param _nonce Replay protection nonce + * @param _signature Signature */ function enqueue( address _target, uint256 _gasLimit, - bytes memory _data + bytes memory _data, + uint _nonce, + bytes memory _signature ) override public @@ -312,9 +318,18 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad i++; } + address signer; + if(_signature.length != 0) { + // Extract signer's address. + bytes32 message = keccak256(abi.encode("enqueue", _target, _gasLimit, _data, address(this), _nonce)); + signer = checkSignatureAndReplayProtection(message, _nonce, _signature); // Throws if signature or replay protection is invalid + } else { + signer = msg.sender; + } + bytes32 transactionHash = keccak256( abi.encode( - msg.sender, + signer, _target, _gasLimit, _data @@ -336,7 +351,7 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad uint256 queueIndex = queue.length() / 2; emit TransactionEnqueued( - msg.sender, + signer, _target, _gasLimit, _data, @@ -348,9 +363,13 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad /** * Appends a given number of queued transactions as a single batch. * @param _numQueuedTransactions Number of transactions to append. + * @param _nonce Replay protection nonce + * @param _signature Signature */ function appendQueueBatch( - uint256 _numQueuedTransactions + uint256 _numQueuedTransactions, + uint _nonce, + bytes memory _signature ) override public @@ -364,11 +383,20 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad "Must append more than zero transactions." ); + address signer; + if(_signature.length != 0) { + // Extract signer's address. + bytes32 message = keccak256(abi.encode("appendQueueBatch", _numQueuedTransactions, address(this), _nonce)); + signer = checkSignatureAndReplayProtection(message, _nonce, _signature); // Throws if signature or replay protection is invalid + } else { + signer = msg.sender; + } + bytes32[] memory leaves = new bytes32[](_numQueuedTransactions); uint40 nextQueueIndex = getNextQueueIndex(); for (uint256 i = 0; i < _numQueuedTransactions; i++) { - if (msg.sender != resolve("OVM_Sequencer")) { + if (signer != resolve("OVM_Sequencer")) { Lib_OVMCodec.QueueElement memory el = getQueueElement(nextQueueIndex); require( el.timestamp + forceInclusionPeriodSeconds < block.timestamp, @@ -422,8 +450,17 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, Lib_Ad "Actual batch start index does not match expected start index." ); + address signer = msg.sender; + // if(_signature.length == 0) { + // // Extract signer's address. + // bytes32 message = keccak256(abi.encode("appendSequencerBatch", address(this), _nonce)); + // signer = checkSignatureAndReplayProtection(message, _nonce, _signature); // Throws if signature or replay protection is invalid + // } else { + // signer = msg.sender; + // } + require( - msg.sender == resolve("OVM_Sequencer"), + signer == resolve("OVM_Sequencer"), "Function can only be called by the Sequencer." ); diff --git a/contracts/optimistic-ethereum/OVM/chain/OVM_StateCommitmentChain.sol b/contracts/optimistic-ethereum/OVM/chain/OVM_StateCommitmentChain.sol index 834c7519b..3fd6f156a 100644 --- a/contracts/optimistic-ethereum/OVM/chain/OVM_StateCommitmentChain.sol +++ b/contracts/optimistic-ethereum/OVM/chain/OVM_StateCommitmentChain.sol @@ -6,6 +6,7 @@ pragma experimental ABIEncoderV2; import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol"; import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol"; import { Lib_MerkleTree } from "../../libraries/utils/Lib_MerkleTree.sol"; +import { ReplayProtection } from "../../libraries/standards/ReplayProtection.sol"; /* Interface Imports */ import { iOVM_FraudVerifier } from "../../iOVM/verification/iOVM_FraudVerifier.sol"; @@ -27,7 +28,7 @@ import '@openzeppelin/contracts/math/SafeMath.sol'; * Compiler used: solc * Runtime target: EVM */ -contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, Lib_AddressResolver { +contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, Lib_AddressResolver, ReplayProtection { /************* * Constants * @@ -126,7 +127,9 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, Lib_AddressResol */ function appendStateBatch( bytes32[] memory _batch, - uint256 _shouldStartAtElement + uint256 _shouldStartAtElement, + uint _nonce, + bytes memory _signature ) override public @@ -138,9 +141,18 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, Lib_AddressResol "Actual batch start index does not match expected start index." ); + address signer; + if(_signature.length != 0) { + // Extract signer's address. + bytes32 message = keccak256(abi.encode("appendStateBatch", _batch, _shouldStartAtElement, address(this), _nonce)); + signer = checkSignatureAndReplayProtection(message, _nonce, _signature); // Throws if signature or replay protection is invalid + } else { + signer = msg.sender; + } + // Proposers must have previously staked at the BondManager require( - iOVM_BondManager(resolve("OVM_BondManager")).isCollateralized(msg.sender), + iOVM_BondManager(resolve("OVM_BondManager")).isCollateralized(signer), "Proposer does not have enough collateral posted" ); @@ -158,7 +170,8 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, Lib_AddressResol // to be used in the fraud proofs _appendBatch( _batch, - abi.encode(block.timestamp, msg.sender) + abi.encode(block.timestamp, signer), + signer ); } @@ -166,13 +179,25 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, Lib_AddressResol * @inheritdoc iOVM_StateCommitmentChain */ function deleteStateBatch( - Lib_OVMCodec.ChainBatchHeader memory _batchHeader + Lib_OVMCodec.ChainBatchHeader memory _batchHeader, + uint _nonce, + bytes memory _signature ) override public { + + address signer; + if(_signature.length != 0) { + // Extract signer's address. + bytes32 message = keccak256(abi.encode("deleteStateBatch", _batchHeader, address(this), _nonce)); + signer = checkSignatureAndReplayProtection(message, _nonce, _signature); // Throws if signature or replay protection is invalid + } else { + signer = msg.sender; + } + require( - msg.sender == resolve("OVM_FraudVerifier"), + signer == resolve("OVM_FraudVerifier"), "State batches can only be deleted by the OVM_FraudVerifier." ); @@ -312,17 +337,19 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, Lib_AddressResol * Appends a batch to the chain. * @param _batch Elements within the batch. * @param _extraData Any extra data to append to the batch. + * @param _signer Signer's address (verified in parent call). */ function _appendBatch( bytes32[] memory _batch, - bytes memory _extraData + bytes memory _extraData, + address _signer ) internal { address sequencer = resolve("OVM_Sequencer"); (uint40 totalElements, uint40 lastSequencerTimestamp) = _getBatchExtraData(); - if (msg.sender == sequencer) { + if (_signer == sequencer) { lastSequencerTimestamp = uint40(block.timestamp); } else { // We keep track of the last batch submitted by the sequencer so there's a window in diff --git a/contracts/optimistic-ethereum/OVM/verification/OVM_FraudVerifier.sol b/contracts/optimistic-ethereum/OVM/verification/OVM_FraudVerifier.sol index 4b0f2609e..96bce3870 100644 --- a/contracts/optimistic-ethereum/OVM/verification/OVM_FraudVerifier.sol +++ b/contracts/optimistic-ethereum/OVM/verification/OVM_FraudVerifier.sol @@ -282,7 +282,7 @@ contract OVM_FraudVerifier is Lib_AddressResolver, Abs_FraudContributor, iOVM_Fr // Delete the state batch. ovmStateCommitmentChain.deleteStateBatch( - _postStateRootBatchHeader + _postStateRootBatchHeader, 0, "" ); // Get the timestamp and publisher for that block. diff --git a/contracts/optimistic-ethereum/iOVM/chain/iOVM_CanonicalTransactionChain.sol b/contracts/optimistic-ethereum/iOVM/chain/iOVM_CanonicalTransactionChain.sol index 94de09a37..d3b267212 100644 --- a/contracts/optimistic-ethereum/iOVM/chain/iOVM_CanonicalTransactionChain.sol +++ b/contracts/optimistic-ethereum/iOVM/chain/iOVM_CanonicalTransactionChain.sol @@ -184,20 +184,28 @@ interface iOVM_CanonicalTransactionChain { * @param _target Target contract to send the transaction to. * @param _gasLimit Gas limit for the given transaction. * @param _data Transaction data. + * @param _nonce Replay protection nonce. + * @param _signature Signature. */ function enqueue( address _target, uint256 _gasLimit, - bytes memory _data + bytes memory _data, + uint _nonce, + bytes memory _signature ) external; /** * Appends a given number of queued transactions as a single batch. * @param _numQueuedTransactions Number of transactions to append. + * @param _nonce Replay protection nonce. + * @param _signature Signature. */ function appendQueueBatch( - uint256 _numQueuedTransactions + uint256 _numQueuedTransactions, + uint _nonce, + bytes memory _signature ) external; diff --git a/contracts/optimistic-ethereum/iOVM/chain/iOVM_StateCommitmentChain.sol b/contracts/optimistic-ethereum/iOVM/chain/iOVM_StateCommitmentChain.sol index b90f00dc6..3070c37cc 100644 --- a/contracts/optimistic-ethereum/iOVM/chain/iOVM_StateCommitmentChain.sol +++ b/contracts/optimistic-ethereum/iOVM/chain/iOVM_StateCommitmentChain.sol @@ -69,19 +69,27 @@ interface iOVM_StateCommitmentChain { * Appends a batch of state roots to the chain. * @param _batch Batch of state roots. * @param _shouldStartAtElement Index of the element at which this batch should start. + * @param _nonce Replay protection nonce. + * @param _signature Signature. */ function appendStateBatch( bytes32[] calldata _batch, - uint256 _shouldStartAtElement + uint256 _shouldStartAtElement, + uint _nonce, + bytes memory _signature ) external; /** * Deletes all state roots after (and including) a given batch. * @param _batchHeader Header of the batch to start deleting from. + * @param _nonce Replay protection nonce. + * @param _signature Signature. */ function deleteStateBatch( - Lib_OVMCodec.ChainBatchHeader memory _batchHeader + Lib_OVMCodec.ChainBatchHeader memory _batchHeader, + uint _nonce, + bytes memory _signature ) external; diff --git a/contracts/optimistic-ethereum/libraries/standards/ReplayProtection.sol b/contracts/optimistic-ethereum/libraries/standards/ReplayProtection.sol new file mode 100644 index 000000000..0ebee19f0 --- /dev/null +++ b/contracts/optimistic-ethereum/libraries/standards/ReplayProtection.sol @@ -0,0 +1,29 @@ +pragma solidity >=0.5.16 <0.8.0; + +/* External Imports */ +import "@openzeppelin/contracts/cryptography/ECDSA.sol"; + +contract ReplayProtection { + + /********************* + * Replay protection * + ********************/ + mapping(address => uint) nonce; + + /** + * Throws if the replay protection is incorrect for the signer. + * It will check that the submitted nonce is greater than the nonce stored. Unlike Ethereum which requires it + * to strictly increment by 1. + * + * @param _hash Hash of message to be signed. + * @param _nonce Replay protection nonce. + * @param _signature Signature to verify. + * @return Signer's address. + */ + function checkSignatureAndReplayProtection(bytes32 _hash, uint _nonce, bytes memory _signature) internal returns(address) { + address signer = ECDSA.recover(_hash, _signature); + require(_nonce > nonce[signer], "Transaction already submitted by signer"); + nonce[signer] = _nonce; // Re-use storage to minimise gas cost + return signer; + } +} diff --git a/hardhat.config.ts b/hardhat.config.ts index 37365517a..162a91d27 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -1,16 +1,16 @@ -import { HardhatUserConfig } from 'hardhat/types' +import { HardhatUserConfig } from "hardhat/types"; import { DEFAULT_ACCOUNTS_HARDHAT, RUN_OVM_TEST_GAS, -} from './test/helpers/constants' +} from "./test/helpers/constants"; // Hardhat plugins -import '@nomiclabs/hardhat-ethers' -import '@nomiclabs/hardhat-waffle' -import 'hardhat-typechain' -import '@eth-optimism/plugins/hardhat/compiler' -import '@eth-optimism/smock/build/src/plugins/hardhat-storagelayout' +import "@nomiclabs/hardhat-ethers"; +import "@nomiclabs/hardhat-waffle"; +import "hardhat-typechain"; +import "@eth-optimism/plugins/hardhat/compiler"; +import "@eth-optimism/smock/build/src/plugins/hardhat-storagelayout"; const config: HardhatUserConfig = { networks: { @@ -23,15 +23,15 @@ const config: HardhatUserConfig = { timeout: 50000, }, solidity: { - version: '0.7.6', + version: "0.7.6", settings: { optimizer: { enabled: true, runs: 200 }, }, }, typechain: { - outDir: 'build/types', - target: 'ethers-v5', + outDir: "src/types", + target: "ethers-v5", }, -} +}; -export default config +export default config; diff --git a/test/contracts/OVM/bridge/base/OVM_L1CrossDomainMessenger.spec.ts b/test/contracts/OVM/bridge/base/OVM_L1CrossDomainMessenger.spec.ts index 1f9ce6741..8b0191466 100644 --- a/test/contracts/OVM/bridge/base/OVM_L1CrossDomainMessenger.spec.ts +++ b/test/contracts/OVM/bridge/base/OVM_L1CrossDomainMessenger.spec.ts @@ -1,10 +1,10 @@ -import { expect } from '../../../../setup' +import { expect } from "../../../../setup"; /* External Imports */ -import { ethers } from 'hardhat' -import { Signer, ContractFactory, Contract, BigNumber } from 'ethers' -import { smockit, MockContract } from '@eth-optimism/smock' -import { remove0x, toHexString } from '@eth-optimism/core-utils' +import { ethers } from "hardhat"; +import { Signer, ContractFactory, Contract, BigNumber } from "ethers"; +import { smockit, MockContract } from "@eth-optimism/smock"; +import { remove0x, toHexString } from "@eth-optimism/core-utils"; /* Internal Imports */ import { @@ -18,121 +18,128 @@ import { TrieTestGenerator, getNextBlockNumber, getXDomainCalldata, -} from '../../../../helpers' -import { keccak256 } from 'ethers/lib/utils' +} from "../../../../helpers"; +import { keccak256 } from "ethers/lib/utils"; const deployProxyXDomainMessenger = async ( addressManager: Contract, l1XDomainMessenger: Contract ): Promise => { await addressManager.setAddress( - 'OVM_L1CrossDomainMessenger', + "OVM_L1CrossDomainMessenger", l1XDomainMessenger.address - ) + ); const proxy = await ( - await ethers.getContractFactory('Lib_ResolvedDelegateProxy') - ).deploy(addressManager.address, 'OVM_L1CrossDomainMessenger') - return l1XDomainMessenger.attach(proxy.address) -} + await ethers.getContractFactory("Lib_ResolvedDelegateProxy") + ).deploy(addressManager.address, "OVM_L1CrossDomainMessenger"); + return l1XDomainMessenger.attach(proxy.address); +}; -describe('OVM_L1CrossDomainMessenger', () => { - let signer: Signer +describe("OVM_L1CrossDomainMessenger", () => { + let signer: Signer; before(async () => { - ;[signer] = await ethers.getSigners() - }) + [signer] = await ethers.getSigners(); + }); - let AddressManager: Contract + let AddressManager: Contract; before(async () => { - AddressManager = await makeAddressManager() - }) + AddressManager = await makeAddressManager(); + }); - let Mock__TargetContract: MockContract - let Mock__OVM_L2CrossDomainMessenger: MockContract - let Mock__OVM_CanonicalTransactionChain: MockContract - let Mock__OVM_StateCommitmentChain: MockContract + let Mock__TargetContract: MockContract; + let Mock__OVM_L2CrossDomainMessenger: MockContract; + let Mock__OVM_CanonicalTransactionChain: MockContract; + let Mock__OVM_StateCommitmentChain: MockContract; before(async () => { Mock__TargetContract = await smockit( - await ethers.getContractFactory('Helper_SimpleProxy') - ) + await ethers.getContractFactory("Helper_SimpleProxy") + ); Mock__OVM_L2CrossDomainMessenger = await smockit( - await ethers.getContractFactory('OVM_L2CrossDomainMessenger') - ) + await ethers.getContractFactory("OVM_L2CrossDomainMessenger") + ); Mock__OVM_CanonicalTransactionChain = await smockit( - await ethers.getContractFactory('OVM_CanonicalTransactionChain') - ) + await ethers.getContractFactory("OVM_CanonicalTransactionChain") + ); Mock__OVM_StateCommitmentChain = await smockit( - await ethers.getContractFactory('OVM_StateCommitmentChain') - ) + await ethers.getContractFactory("OVM_StateCommitmentChain") + ); await AddressManager.setAddress( - 'OVM_L2CrossDomainMessenger', + "OVM_L2CrossDomainMessenger", Mock__OVM_L2CrossDomainMessenger.address - ) + ); await setProxyTarget( AddressManager, - 'OVM_CanonicalTransactionChain', + "OVM_CanonicalTransactionChain", Mock__OVM_CanonicalTransactionChain - ) + ); await setProxyTarget( AddressManager, - 'OVM_StateCommitmentChain', + "OVM_StateCommitmentChain", Mock__OVM_StateCommitmentChain - ) - }) + ); + }); - let Factory__OVM_L1CrossDomainMessenger: ContractFactory + let Factory__OVM_L1CrossDomainMessenger: ContractFactory; before(async () => { Factory__OVM_L1CrossDomainMessenger = await ethers.getContractFactory( - 'OVM_L1CrossDomainMessenger' - ) - }) + "OVM_L1CrossDomainMessenger" + ); + }); - let OVM_L1CrossDomainMessenger: Contract + let OVM_L1CrossDomainMessenger: Contract; beforeEach(async () => { - const xDomainMessenerImpl = await Factory__OVM_L1CrossDomainMessenger.deploy() + const xDomainMessenerImpl = await Factory__OVM_L1CrossDomainMessenger.deploy(); // We use an upgradable proxy for the XDomainMessenger--deploy & set up the proxy. OVM_L1CrossDomainMessenger = await deployProxyXDomainMessenger( AddressManager, xDomainMessenerImpl - ) - await OVM_L1CrossDomainMessenger.initialize(AddressManager.address) - }) + ); + await OVM_L1CrossDomainMessenger.initialize(AddressManager.address); + }); - describe('sendMessage', () => { - const target = NON_ZERO_ADDRESS - const message = NON_NULL_BYTES32 - const gasLimit = 100_000 + describe("sendMessage", () => { + const target = NON_ZERO_ADDRESS; + const message = NON_NULL_BYTES32; + const gasLimit = 100_000; - it('should be able to send a single message', async () => { + it("should be able to send a single message", async () => { await expect( OVM_L1CrossDomainMessenger.sendMessage(target, message, gasLimit) - ).to.not.be.reverted + ).to.not.be.reverted; + console.log( + JSON.stringify( + Mock__OVM_CanonicalTransactionChain.smocked.enqueue.calls[0] + ) + ); expect( Mock__OVM_CanonicalTransactionChain.smocked.enqueue.calls[0] ).to.deep.equal([ Mock__OVM_L2CrossDomainMessenger.address, BigNumber.from(gasLimit), getXDomainCalldata(await signer.getAddress(), target, message, 0), - ]) - }) + BigNumber.from("0"), + "0x", + ]); + }); - it('should be able to send the same message twice', async () => { - await OVM_L1CrossDomainMessenger.sendMessage(target, message, gasLimit) + it("should be able to send the same message twice", async () => { + await OVM_L1CrossDomainMessenger.sendMessage(target, message, gasLimit); await expect( OVM_L1CrossDomainMessenger.sendMessage(target, message, gasLimit) - ).to.not.be.reverted - }) - }) + ).to.not.be.reverted; + }); + }); - describe('replayMessage', () => { - const target = NON_ZERO_ADDRESS - const message = NON_NULL_BYTES32 - const gasLimit = 100_000 + describe("replayMessage", () => { + const target = NON_ZERO_ADDRESS; + const message = NON_NULL_BYTES32; + const gasLimit = 100_000; - it('should revert if the message does not exist', async () => { + it("should revert if the message does not exist", async () => { await expect( OVM_L1CrossDomainMessenger.replayMessage( target, @@ -141,11 +148,11 @@ describe('OVM_L1CrossDomainMessenger', () => { 0, gasLimit ) - ).to.be.revertedWith('Provided message has not already been sent.') - }) + ).to.be.revertedWith("Provided message has not already been sent."); + }); - it('should succeed if the message exists', async () => { - await OVM_L1CrossDomainMessenger.sendMessage(target, message, gasLimit) + it("should succeed if the message exists", async () => { + await OVM_L1CrossDomainMessenger.sendMessage(target, message, gasLimit); await expect( OVM_L1CrossDomainMessenger.replayMessage( @@ -155,41 +162,41 @@ describe('OVM_L1CrossDomainMessenger', () => { 0, gasLimit ) - ).to.not.be.reverted - }) - }) - - describe('relayMessage', () => { - let target: string - let message: string - let sender: string - let proof: any - let calldata: string + ).to.not.be.reverted; + }); + }); + + describe("relayMessage", () => { + let target: string; + let message: string; + let sender: string; + let proof: any; + let calldata: string; before(async () => { - target = Mock__TargetContract.address - message = Mock__TargetContract.interface.encodeFunctionData('setTarget', [ + target = Mock__TargetContract.address; + message = Mock__TargetContract.interface.encodeFunctionData("setTarget", [ NON_ZERO_ADDRESS, - ]) - sender = await signer.getAddress() + ]); + sender = await signer.getAddress(); - calldata = getXDomainCalldata(sender, target, message, 0) + calldata = getXDomainCalldata(sender, target, message, 0); - const precompile = '0x4200000000000000000000000000000000000000' + const precompile = "0x4200000000000000000000000000000000000000"; const storageKey = keccak256( keccak256( calldata + remove0x(Mock__OVM_L2CrossDomainMessenger.address) - ) + '00'.repeat(32) - ) + ) + "00".repeat(32) + ); const storageGenerator = await TrieTestGenerator.fromNodes({ nodes: [ { key: storageKey, - val: '0x' + '01'.padStart(2, '0'), + val: "0x" + "01".padStart(2, "0"), }, ], secure: true, - }) + }); const generator = await TrieTestGenerator.fromAccounts({ accounts: [ @@ -197,12 +204,12 @@ describe('OVM_L1CrossDomainMessenger', () => { address: precompile, nonce: 0, balance: 0, - codeHash: keccak256('0x1234'), + codeHash: keccak256("0x1234"), storageRoot: toHexString(storageGenerator._trie.root), }, ], secure: true, - }) + }); proof = { stateRoot: toHexString(generator._trie.root), @@ -213,30 +220,30 @@ describe('OVM_L1CrossDomainMessenger', () => { storageTrieWitness: ( await storageGenerator.makeInclusionProofTest(storageKey) ).proof, - } - }) + }; + }); beforeEach(async () => { Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with( true - ) + ); Mock__OVM_StateCommitmentChain.smocked.insideFraudProofWindow.will.return.with( false - ) - }) + ); + }); - it('should revert if still inside the fraud proof window', async () => { + it("should revert if still inside the fraud proof window", async () => { Mock__OVM_StateCommitmentChain.smocked.insideFraudProofWindow.will.return.with( true - ) + ); const proof1 = { stateRoot: NULL_BYTES32, stateRootBatchHeader: DUMMY_BATCH_HEADERS[0], stateRootProof: DUMMY_BATCH_PROOFS[0], - stateTrieWitness: '0x', - storageTrieWitness: '0x', - } + stateTrieWitness: "0x", + storageTrieWitness: "0x", + }; await expect( OVM_L1CrossDomainMessenger.relayMessage( @@ -246,21 +253,21 @@ describe('OVM_L1CrossDomainMessenger', () => { 0, proof1 ) - ).to.be.revertedWith('Provided message could not be verified.') - }) + ).to.be.revertedWith("Provided message could not be verified."); + }); - it('should revert if provided an invalid state root proof', async () => { + it("should revert if provided an invalid state root proof", async () => { Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with( false - ) + ); const proof1 = { stateRoot: NULL_BYTES32, stateRootBatchHeader: DUMMY_BATCH_HEADERS[0], stateRootProof: DUMMY_BATCH_PROOFS[0], - stateTrieWitness: '0x', - storageTrieWitness: '0x', - } + stateTrieWitness: "0x", + storageTrieWitness: "0x", + }; await expect( OVM_L1CrossDomainMessenger.relayMessage( @@ -270,29 +277,29 @@ describe('OVM_L1CrossDomainMessenger', () => { 0, proof1 ) - ).to.be.revertedWith('Provided message could not be verified.') - }) + ).to.be.revertedWith("Provided message could not be verified."); + }); - it('should revert if provided an invalid storage trie witness', async () => { + it("should revert if provided an invalid storage trie witness", async () => { await expect( OVM_L1CrossDomainMessenger.relayMessage(target, sender, message, 0, { ...proof, - storageTrieWitness: '0x', + storageTrieWitness: "0x", }) - ).to.be.reverted - }) + ).to.be.reverted; + }); - it('should revert if provided an invalid state trie witness', async () => { + it("should revert if provided an invalid state trie witness", async () => { await expect( OVM_L1CrossDomainMessenger.relayMessage(target, sender, message, 0, { ...proof, - stateTrieWitness: '0x', + stateTrieWitness: "0x", }) - ).to.be.reverted - }) + ).to.be.reverted; + }); - it('should send a successful call to the target contract', async () => { - const blockNumber = await getNextBlockNumber(ethers.provider) + it("should send a successful call to the target contract", async () => { + const blockNumber = await getNextBlockNumber(ethers.provider); await OVM_L1CrossDomainMessenger.relayMessage( target, @@ -300,11 +307,11 @@ describe('OVM_L1CrossDomainMessenger', () => { message, 0, proof - ) + ); expect( await OVM_L1CrossDomainMessenger.successfulMessages(keccak256(calldata)) - ).to.equal(true) + ).to.equal(true); expect( await OVM_L1CrossDomainMessenger.relayedMessages( @@ -313,21 +320,21 @@ describe('OVM_L1CrossDomainMessenger', () => { remove0x(await signer.getAddress()) + remove0x(BigNumber.from(blockNumber).toHexString()).padStart( 64, - '0' + "0" ) ) ) - ).to.equal(true) - }) + ).to.equal(true); + }); - it('should revert if trying to send the same message twice', async () => { + it("should revert if trying to send the same message twice", async () => { await OVM_L1CrossDomainMessenger.relayMessage( target, sender, message, 0, proof - ) + ); await expect( OVM_L1CrossDomainMessenger.relayMessage( @@ -337,15 +344,15 @@ describe('OVM_L1CrossDomainMessenger', () => { 0, proof ) - ).to.be.revertedWith('Provided message has already been received.') - }) + ).to.be.revertedWith("Provided message has already been received."); + }); - it('when the OVM_L2MessageRelayer address is set, should revert if called by a different account', async () => { + it("when the OVM_L2MessageRelayer address is set, should revert if called by a different account", async () => { // set to a random NON-ZERO address await AddressManager.setAddress( - 'OVM_L2MessageRelayer', - '0x1234123412341234123412341234123412341234' - ) + "OVM_L2MessageRelayer", + "0x1234123412341234123412341234123412341234" + ); await expect( OVM_L1CrossDomainMessenger.relayMessage( @@ -356,8 +363,8 @@ describe('OVM_L1CrossDomainMessenger', () => { proof ) ).to.be.revertedWith( - 'Only OVM_L2MessageRelayer can relay L2-to-L1 messages.' - ) - }) - }) -}) + "Only OVM_L2MessageRelayer can relay L2-to-L1 messages." + ); + }); + }); +}); diff --git a/test/contracts/OVM/chain/OVM_CanonicalTransactionChain.spec.ts b/test/contracts/OVM/chain/OVM_CanonicalTransactionChain.spec.ts index 440b94330..7fa5e8cd4 100644 --- a/test/contracts/OVM/chain/OVM_CanonicalTransactionChain.spec.ts +++ b/test/contracts/OVM/chain/OVM_CanonicalTransactionChain.spec.ts @@ -1,12 +1,12 @@ -import { expect } from '../../../setup' +import { expect } from "../../../setup"; /* External Imports */ -import { ethers } from 'hardhat' -import { Signer, ContractFactory, Contract, BigNumber } from 'ethers' -import { TransactionResponse } from '@ethersproject/abstract-provider' -import { smockit, MockContract } from '@eth-optimism/smock' -import { remove0x } from '@eth-optimism/core-utils' -import _ from 'lodash' +import { ethers } from "hardhat"; +import { Signer, ContractFactory, Contract, BigNumber } from "ethers"; +import { TransactionResponse } from "@ethersproject/abstract-provider"; +import { smockit, MockContract } from "@eth-optimism/smock"; +import { remove0x } from "@eth-optimism/core-utils"; +import _ from "lodash"; /* Internal Imports */ import { @@ -22,21 +22,21 @@ import { getBlockTime, ZERO_ADDRESS, mineBlock, -} from '../../../helpers' -import { defaultAbiCoder, keccak256 } from 'ethers/lib/utils' +} from "../../../helpers"; +import { defaultAbiCoder, keccak256 } from "ethers/lib/utils"; -const ELEMENT_TEST_SIZES = [1, 2, 4, 8, 16] -const DECOMPRESSION_ADDRESS = '0x4200000000000000000000000000000000000008' -const MAX_GAS_LIMIT = 8_000_000 +const ELEMENT_TEST_SIZES = [1, 2, 4, 8, 16]; +const DECOMPRESSION_ADDRESS = "0x4200000000000000000000000000000000000008"; +const MAX_GAS_LIMIT = 8_000_000; const getQueueLeafHash = (index: number): string => { return keccak256( defaultAbiCoder.encode( - ['bool', 'uint256', 'uint256', 'uint256', 'bytes'], - [false, index, 0, 0, '0x'] + ["bool", "uint256", "uint256", "uint256", "bytes"], + [false, index, 0, 0, "0x"] ) - ) -} + ); +}; const getSequencerLeafHash = ( timestamp: number, @@ -44,12 +44,12 @@ const getSequencerLeafHash = ( data: string ): string => { return keccak256( - '0x01' + - remove0x(BigNumber.from(timestamp).toHexString()).padStart(64, '0') + - remove0x(BigNumber.from(blockNumber).toHexString()).padStart(64, '0') + + "0x01" + + remove0x(BigNumber.from(timestamp).toHexString()).padStart(64, "0") + + remove0x(BigNumber.from(blockNumber).toHexString()).padStart(64, "0") + remove0x(data) - ) -} + ); +}; const getTransactionHash = ( sender: string, @@ -57,8 +57,8 @@ const getTransactionHash = ( gasLimit: number, data: string ): string => { - return keccak256(encodeQueueTransaction(sender, target, gasLimit, data)) -} + return keccak256(encodeQueueTransaction(sender, target, gasLimit, data)); +}; const encodeQueueTransaction = ( sender: string, @@ -67,290 +67,320 @@ const encodeQueueTransaction = ( data: string ): string => { return defaultAbiCoder.encode( - ['address', 'address', 'uint256', 'bytes'], + ["address", "address", "uint256", "bytes"], [sender, target, gasLimit, data] - ) -} + ); +}; interface BatchContext { - numSequencedTransactions: number - numSubsequentQueueTransactions: number - timestamp: number - blockNumber: number + numSequencedTransactions: number; + numSubsequentQueueTransactions: number; + timestamp: number; + blockNumber: number; } interface AppendSequencerBatchParams { - shouldStartAtElement: number // 5 bytes -- starts at batch - totalElementsToAppend: number // 3 bytes -- total_elements_to_append - contexts: BatchContext[] // total_elements[fixed_size[]] - transactions: string[] // total_size_bytes[],total_size_bytes[] + shouldStartAtElement: number; // 5 bytes -- starts at batch + totalElementsToAppend: number; // 3 bytes -- total_elements_to_append + contexts: BatchContext[]; // total_elements[fixed_size[]] + transactions: string[]; // total_size_bytes[],total_size_bytes[] } const encodeAppendSequencerBatch = (b: AppendSequencerBatchParams): string => { const encodedShouldStartAtElement = remove0x( BigNumber.from(b.shouldStartAtElement).toHexString() - ).padStart(10, '0') + ).padStart(10, "0"); const encodedTotalElementsToAppend = remove0x( BigNumber.from(b.totalElementsToAppend).toHexString() - ).padStart(6, '0') + ).padStart(6, "0"); const encodedContextsHeader = remove0x( BigNumber.from(b.contexts.length).toHexString() - ).padStart(6, '0') + ).padStart(6, "0"); const encodedContexts = encodedContextsHeader + - b.contexts.reduce((acc, cur) => acc + encodeBatchContext(cur), '') + b.contexts.reduce((acc, cur) => acc + encodeBatchContext(cur), ""); const encodedTransactionData = b.transactions.reduce((acc, cur) => { if (cur.length % 2 !== 0) - throw new Error('Unexpected uneven hex string value!') + throw new Error("Unexpected uneven hex string value!"); const encodedTxDataHeader = remove0x( BigNumber.from(remove0x(cur).length / 2).toHexString() - ).padStart(6, '0') - return acc + encodedTxDataHeader + remove0x(cur) - }, '') + ).padStart(6, "0"); + + return acc + encodedTxDataHeader + remove0x(cur); + }, ""); return ( encodedShouldStartAtElement + encodedTotalElementsToAppend + encodedContexts + encodedTransactionData - ) -} + ); +}; const appendSequencerBatch = async ( OVM_CanonicalTransactionChain: Contract, batch: AppendSequencerBatchParams ): Promise => { - const methodId = keccak256(Buffer.from('appendSequencerBatch()')).slice(2, 10) - const calldata = encodeAppendSequencerBatch(batch) + const methodId = keccak256(Buffer.from("appendSequencerBatch()")).slice( + 2, + 10 + ); + const calldata = encodeAppendSequencerBatch(batch); return OVM_CanonicalTransactionChain.signer.sendTransaction({ to: OVM_CanonicalTransactionChain.address, - data: '0x' + methodId + calldata, - }) -} + data: "0x" + methodId + calldata, + }); +}; const encodeBatchContext = (context: BatchContext): string => { return ( remove0x( BigNumber.from(context.numSequencedTransactions).toHexString() - ).padStart(6, '0') + + ).padStart(6, "0") + remove0x( BigNumber.from(context.numSubsequentQueueTransactions).toHexString() - ).padStart(6, '0') + + ).padStart(6, "0") + remove0x(BigNumber.from(context.timestamp).toHexString()).padStart( 10, - '0' + "0" ) + remove0x(BigNumber.from(context.blockNumber).toHexString()).padStart( 10, - '0' + "0" ) - ) -} + ); +}; -describe('OVM_CanonicalTransactionChain', () => { - let signer: Signer - let sequencer: Signer +describe("OVM_CanonicalTransactionChain", () => { + let signer: Signer; + let sequencer: Signer; before(async () => { - ;[signer, sequencer] = await ethers.getSigners() - }) + [signer, sequencer] = await ethers.getSigners(); + }); - let AddressManager: Contract - let Mock__OVM_ExecutionManager: MockContract - let Mock__OVM_StateCommitmentChain: MockContract + let AddressManager: Contract; + let Mock__OVM_ExecutionManager: MockContract; + let Mock__OVM_StateCommitmentChain: MockContract; before(async () => { - AddressManager = await makeAddressManager() + AddressManager = await makeAddressManager(); await AddressManager.setAddress( - 'OVM_Sequencer', + "OVM_Sequencer", await sequencer.getAddress() - ) + ); await AddressManager.setAddress( - 'OVM_DecompressionPrecompileAddress', + "OVM_DecompressionPrecompileAddress", DECOMPRESSION_ADDRESS - ) + ); Mock__OVM_ExecutionManager = await smockit( - await ethers.getContractFactory('OVM_ExecutionManager') - ) + await ethers.getContractFactory("OVM_ExecutionManager") + ); Mock__OVM_StateCommitmentChain = await smockit( - await ethers.getContractFactory('OVM_StateCommitmentChain') - ) + await ethers.getContractFactory("OVM_StateCommitmentChain") + ); await setProxyTarget( AddressManager, - 'OVM_ExecutionManager', + "OVM_ExecutionManager", Mock__OVM_ExecutionManager - ) + ); await setProxyTarget( AddressManager, - 'OVM_StateCommitmentChain', + "OVM_StateCommitmentChain", Mock__OVM_StateCommitmentChain - ) + ); Mock__OVM_ExecutionManager.smocked.getMaxTransactionGasLimit.will.return.with( MAX_GAS_LIMIT - ) - }) + ); + }); - let Factory__OVM_CanonicalTransactionChain: ContractFactory - let Factory__OVM_ChainStorageContainer: ContractFactory + let Factory__OVM_CanonicalTransactionChain: ContractFactory; + let Factory__OVM_ChainStorageContainer: ContractFactory; before(async () => { Factory__OVM_CanonicalTransactionChain = await ethers.getContractFactory( - 'OVM_CanonicalTransactionChain' - ) + "OVM_CanonicalTransactionChain" + ); Factory__OVM_ChainStorageContainer = await ethers.getContractFactory( - 'OVM_ChainStorageContainer' - ) - }) + "OVM_ChainStorageContainer" + ); + }); - let OVM_CanonicalTransactionChain: Contract + let OVM_CanonicalTransactionChain: Contract; beforeEach(async () => { OVM_CanonicalTransactionChain = await Factory__OVM_CanonicalTransactionChain.deploy( AddressManager.address, FORCE_INCLUSION_PERIOD_SECONDS, FORCE_INCLUSION_PERIOD_BLOCKS, MAX_GAS_LIMIT - ) + ); const batches = await Factory__OVM_ChainStorageContainer.deploy( AddressManager.address, - 'OVM_CanonicalTransactionChain' - ) + "OVM_CanonicalTransactionChain" + ); const queue = await Factory__OVM_ChainStorageContainer.deploy( AddressManager.address, - 'OVM_CanonicalTransactionChain' - ) + "OVM_CanonicalTransactionChain" + ); await AddressManager.setAddress( - 'OVM_ChainStorageContainer:CTC:batches', + "OVM_ChainStorageContainer:CTC:batches", batches.address - ) + ); await AddressManager.setAddress( - 'OVM_ChainStorageContainer:CTC:queue', + "OVM_ChainStorageContainer:CTC:queue", queue.address - ) + ); await AddressManager.setAddress( - 'OVM_CanonicalTransactionChain', + "OVM_CanonicalTransactionChain", OVM_CanonicalTransactionChain.address - ) - }) + ); + }); - describe('enqueue', () => { - const target = NON_ZERO_ADDRESS - const gasLimit = 500_000 + describe("enqueue", () => { + const target = NON_ZERO_ADDRESS; + const gasLimit = 500_000; - it('should revert when trying to input more data than the max data size', async () => { - const MAX_ROLLUP_TX_SIZE = await OVM_CanonicalTransactionChain.MAX_ROLLUP_TX_SIZE() - const data = '0x' + '12'.repeat(MAX_ROLLUP_TX_SIZE + 1) + it("should revert when trying to input more data than the max data size", async () => { + const MAX_ROLLUP_TX_SIZE = await OVM_CanonicalTransactionChain.MAX_ROLLUP_TX_SIZE(); + const data = "0x" + "12".repeat(MAX_ROLLUP_TX_SIZE + 1); await expect( - OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data) + OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data, 0, "0x") ).to.be.revertedWith( - 'Transaction data size exceeds maximum for rollup transaction.' - ) - }) + "Transaction data size exceeds maximum for rollup transaction." + ); + }); - it('should revert when trying to enqueue a transaction with a higher gasLimit than the max', async () => { - const data = '0x1234567890' + it("should revert when trying to enqueue a transaction with a higher gasLimit than the max", async () => { + const data = "0x1234567890"; await expect( - OVM_CanonicalTransactionChain.enqueue(target, MAX_GAS_LIMIT + 1, data) + OVM_CanonicalTransactionChain.enqueue( + target, + MAX_GAS_LIMIT + 1, + data, + 0, + "0x" + ) ).to.be.revertedWith( - 'Transaction gas limit exceeds maximum for rollup transaction.' - ) - }) + "Transaction gas limit exceeds maximum for rollup transaction." + ); + }); - it('should revert if gas limit parameter is not at least MIN_ROLLUP_TX_GAS', async () => { - const MIN_ROLLUP_TX_GAS = await OVM_CanonicalTransactionChain.MIN_ROLLUP_TX_GAS() - const customGasLimit = MIN_ROLLUP_TX_GAS / 2 - const data = '0x' + '12'.repeat(1234) + it("should revert if gas limit parameter is not at least MIN_ROLLUP_TX_GAS", async () => { + const MIN_ROLLUP_TX_GAS = await OVM_CanonicalTransactionChain.MIN_ROLLUP_TX_GAS(); + const customGasLimit = MIN_ROLLUP_TX_GAS / 2; + const data = "0x" + "12".repeat(1234); await expect( - OVM_CanonicalTransactionChain.enqueue(target, customGasLimit, data) - ).to.be.revertedWith('Transaction gas limit too low to enqueue.') - }) + OVM_CanonicalTransactionChain.enqueue( + target, + customGasLimit, + data, + 0, + "0x" + ) + ).to.be.revertedWith("Transaction gas limit too low to enqueue."); + }); - it('should revert if transaction gas limit does not cover rollup burn', async () => { - const L2_GAS_DISCOUNT_DIVISOR = await OVM_CanonicalTransactionChain.L2_GAS_DISCOUNT_DIVISOR() - const data = '0x' + '12'.repeat(1234) + it("should revert if transaction gas limit does not cover rollup burn", async () => { + const L2_GAS_DISCOUNT_DIVISOR = await OVM_CanonicalTransactionChain.L2_GAS_DISCOUNT_DIVISOR(); + const data = "0x" + "12".repeat(1234); await expect( - OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data, { + OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data, 0, "0x", { gasLimit: gasLimit / L2_GAS_DISCOUNT_DIVISOR + 30_000, // offset constant overhead }) - ).to.be.revertedWith('Insufficient gas for L2 rate limiting burn.') - }) + ).to.be.revertedWith("Insufficient gas for L2 rate limiting burn."); + }); - describe('with valid input parameters', () => { - it('should emit a TransactionEnqueued event', async () => { - const timestamp = (await getEthTime(ethers.provider)) + 100 - const data = '0x' + '12'.repeat(1234) + describe("with valid input parameters", () => { + it("should emit a TransactionEnqueued event", async () => { + const timestamp = (await getEthTime(ethers.provider)) + 100; + const data = "0x" + "12".repeat(1234); - await setEthTime(ethers.provider, timestamp) + await setEthTime(ethers.provider, timestamp); await expect( - OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data) - ).to.emit(OVM_CanonicalTransactionChain, 'TransactionEnqueued') - }) + OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data, 0, "0x") + ).to.emit(OVM_CanonicalTransactionChain, "TransactionEnqueued"); + }); - describe('when enqueing multiple times', () => { - const data = '0x' + '12'.repeat(1234) + describe("when enqueing multiple times", () => { + const data = "0x" + "12".repeat(1234); for (const size of ELEMENT_TEST_SIZES) { it(`should be able to enqueue ${size} elements`, async () => { for (let i = 0; i < size; i++) { await expect( - OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data) - ).to.not.be.reverted + OVM_CanonicalTransactionChain.enqueue( + target, + gasLimit, + data, + 0, + "0x" + ) + ).to.not.be.reverted; } - }) + }); } - }) - }) - }) + }); + }); + }); - describe('getQueueElement', () => { - it('should revert when accessing a non-existent element', async () => { + describe("getQueueElement", () => { + it("should revert when accessing a non-existent element", async () => { await expect( OVM_CanonicalTransactionChain.getQueueElement(0) - ).to.be.revertedWith('Index out of bounds.') - }) + ).to.be.revertedWith("Index out of bounds."); + }); - describe('when the requested element exists', () => { - const target = NON_ZERO_ADDRESS - const gasLimit = 500_000 - const data = '0x' + '12'.repeat(1234) + describe("when the requested element exists", () => { + const target = NON_ZERO_ADDRESS; + const gasLimit = 500_000; + const data = "0x" + "12".repeat(1234); - describe('when getting the first element', () => { + describe("when getting the first element", () => { for (const size of ELEMENT_TEST_SIZES) { it(`gets the element when ${size} + 1 elements exist`, async () => { - const timestamp = (await getEthTime(ethers.provider)) + 100 - const blockNumber = await getNextBlockNumber(ethers.provider) - await setEthTime(ethers.provider, timestamp) + const timestamp = (await getEthTime(ethers.provider)) + 100; + const blockNumber = await getNextBlockNumber(ethers.provider); + await setEthTime(ethers.provider, timestamp); const queueRoot = getTransactionHash( await signer.getAddress(), target, gasLimit, data - ) + ); - await OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data) + await OVM_CanonicalTransactionChain.enqueue( + target, + gasLimit, + data, + 0, + "0x" + ); for (let i = 0; i < size; i++) { await OVM_CanonicalTransactionChain.enqueue( target, gasLimit, - '0x' + '12'.repeat(i + 1) - ) + "0x" + "12".repeat(i + 1), + 0, + "0x" + ); } expect( @@ -361,43 +391,47 @@ describe('OVM_CanonicalTransactionChain', () => { queueRoot, timestamp, blockNumber, - }) - }) + }); + }); } - }) + }); - describe('when getting the middle element', () => { + describe("when getting the middle element", () => { for (const size of ELEMENT_TEST_SIZES) { it(`gets the element when ${size} elements exist`, async () => { - let timestamp: number - let blockNumber: number - let queueRoot: string + let timestamp: number; + let blockNumber: number; + let queueRoot: string; - const middleIndex = Math.floor(size / 2) + const middleIndex = Math.floor(size / 2); for (let i = 0; i < size; i++) { if (i === middleIndex) { - timestamp = (await getEthTime(ethers.provider)) + 100 - blockNumber = await getNextBlockNumber(ethers.provider) - await setEthTime(ethers.provider, timestamp) + timestamp = (await getEthTime(ethers.provider)) + 100; + blockNumber = await getNextBlockNumber(ethers.provider); + await setEthTime(ethers.provider, timestamp); queueRoot = getTransactionHash( await signer.getAddress(), target, gasLimit, data - ) + ); await OVM_CanonicalTransactionChain.enqueue( target, gasLimit, - data - ) + data, + 0, + "0x" + ); } else { await OVM_CanonicalTransactionChain.enqueue( target, gasLimit, - '0x' + '12'.repeat(i + 1) - ) + "0x" + "12".repeat(i + 1), + 0, + "0x" + ); } } @@ -409,42 +443,46 @@ describe('OVM_CanonicalTransactionChain', () => { queueRoot, timestamp, blockNumber, - }) - }) + }); + }); } - }) + }); - describe('when getting the last element', () => { + describe("when getting the last element", () => { for (const size of ELEMENT_TEST_SIZES) { it(`gets the element when ${size} elements exist`, async () => { - let timestamp: number - let blockNumber: number - let queueRoot: string + let timestamp: number; + let blockNumber: number; + let queueRoot: string; for (let i = 0; i < size; i++) { if (i === size - 1) { - timestamp = (await getEthTime(ethers.provider)) + 100 - blockNumber = await getNextBlockNumber(ethers.provider) - await setEthTime(ethers.provider, timestamp) + timestamp = (await getEthTime(ethers.provider)) + 100; + blockNumber = await getNextBlockNumber(ethers.provider); + await setEthTime(ethers.provider, timestamp); queueRoot = getTransactionHash( await signer.getAddress(), target, gasLimit, data - ) + ); await OVM_CanonicalTransactionChain.enqueue( target, gasLimit, - data - ) + data, + 0, + "0x" + ); } else { await OVM_CanonicalTransactionChain.enqueue( target, gasLimit, - '0x' + '12'.repeat(i + 1) - ) + "0x" + "12".repeat(i + 1), + 0, + "0x" + ); } } @@ -456,38 +494,38 @@ describe('OVM_CanonicalTransactionChain', () => { queueRoot, timestamp, blockNumber, - }) - }) + }); + }); } - }) - }) - }) + }); + }); + }); - describe('appendQueueBatch disabled', () => { - it('should revert', async () => { + describe("appendQueueBatch disabled", () => { + it("should revert", async () => { await expect( - OVM_CanonicalTransactionChain.appendQueueBatch(0) - ).to.be.revertedWith('appendQueueBatch is currently disabled.') - }) - }) + OVM_CanonicalTransactionChain.appendQueueBatch(0, 0, "0x") + ).to.be.revertedWith("appendQueueBatch is currently disabled."); + }); + }); - describe.skip('appendQueueBatch', () => { - it('should revert if trying to append zero transactions', async () => { + describe.skip("appendQueueBatch", () => { + it("should revert if trying to append zero transactions", async () => { await expect( - OVM_CanonicalTransactionChain.appendQueueBatch(0) - ).to.be.revertedWith('Must append more than zero transactions.') - }) + OVM_CanonicalTransactionChain.appendQueueBatch(0, 0, "0x") + ).to.be.revertedWith("Must append more than zero transactions."); + }); - it('should revert if the queue is empty', async () => { + it("should revert if the queue is empty", async () => { await expect( - OVM_CanonicalTransactionChain.appendQueueBatch(1) - ).to.be.revertedWith('Must append more than zero transactions.') - }) + OVM_CanonicalTransactionChain.appendQueueBatch(1, 0, "0x") + ).to.be.revertedWith("Must append more than zero transactions."); + }); - describe('when the queue is not empty', () => { - const target = NON_ZERO_ADDRESS - const gasLimit = 500_000 - const data = '0x' + '12'.repeat(1234) + describe("when the queue is not empty", () => { + const target = NON_ZERO_ADDRESS; + const gasLimit = 500_000; + const data = "0x" + "12".repeat(1234); for (const size of ELEMENT_TEST_SIZES) { describe(`when the queue has ${size} elements`, () => { @@ -496,77 +534,96 @@ describe('OVM_CanonicalTransactionChain', () => { await OVM_CanonicalTransactionChain.enqueue( target, gasLimit, - data - ) + data, + 0, + "0x" + ); } - }) + }); - describe('when the sequencer inclusion period has not passed', () => { - it('should revert if not called by the sequencer', async () => { + describe("when the sequencer inclusion period has not passed", () => { + it("should revert if not called by the sequencer", async () => { await expect( OVM_CanonicalTransactionChain.connect(signer).appendQueueBatch( - 1 + 1, + 0, + "0x" ) ).to.be.revertedWith( - 'Queue transactions cannot be submitted during the sequencer inclusion period.' - ) - }) + "Queue transactions cannot be submitted during the sequencer inclusion period." + ); + }); - it('should succeed if called by the sequencer', async () => { + it("should succeed if called by the sequencer", async () => { await expect( OVM_CanonicalTransactionChain.connect( sequencer - ).appendQueueBatch(1) + ).appendQueueBatch(1, 0, "0x") ) - .to.emit(OVM_CanonicalTransactionChain, 'QueueBatchAppended') - .withArgs(0, 1, 1) - }) - }) + .to.emit(OVM_CanonicalTransactionChain, "QueueBatchAppended") + .withArgs(0, 1, 1); + }); + }); - describe('when the sequencer inclusion period has passed', () => { + describe("when the sequencer inclusion period has passed", () => { beforeEach(async () => { await increaseEthTime( ethers.provider, FORCE_INCLUSION_PERIOD_SECONDS * 2 - ) - }) + ); + }); - it('should be able to append a single element', async () => { - await expect(OVM_CanonicalTransactionChain.appendQueueBatch(1)) - .to.emit(OVM_CanonicalTransactionChain, 'QueueBatchAppended') - .withArgs(0, 1, 1) - }) + it("should be able to append a single element", async () => { + await expect( + OVM_CanonicalTransactionChain.appendQueueBatch(1, 0, "0x") + ) + .to.emit(OVM_CanonicalTransactionChain, "QueueBatchAppended") + .withArgs(0, 1, 1); + }); it(`should be able to append ${size} elements`, async () => { - await expect(OVM_CanonicalTransactionChain.appendQueueBatch(size)) - .to.emit(OVM_CanonicalTransactionChain, 'QueueBatchAppended') - .withArgs(0, size, size) - }) + await expect( + OVM_CanonicalTransactionChain.appendQueueBatch(size, 0, "0x") + ) + .to.emit(OVM_CanonicalTransactionChain, "QueueBatchAppended") + .withArgs(0, size, size); + }); it(`should be able to append ${size} elements even if attempting to append ${size} + 1 elements`, async () => { await expect( - OVM_CanonicalTransactionChain.appendQueueBatch(size + 1) + OVM_CanonicalTransactionChain.appendQueueBatch( + size + 1, + 0, + "0x" + ) ) - .to.emit(OVM_CanonicalTransactionChain, 'QueueBatchAppended') - .withArgs(0, size, size) - }) - }) - }) + .to.emit(OVM_CanonicalTransactionChain, "QueueBatchAppended") + .withArgs(0, size, size); + }); + }); + }); } - }) - }) + }); + }); - describe('verifyTransaction', () => { - it('should successfully verify against a valid queue transaction appended by the sequencer', async () => { - const entrypoint = NON_ZERO_ADDRESS - const gasLimit = 500_000 - const data = '0x' + '12'.repeat(1234) + describe("verifyTransaction", () => { + it("should successfully verify against a valid queue transaction appended by the sequencer", async () => { + const entrypoint = NON_ZERO_ADDRESS; + const gasLimit = 500_000; + const data = "0x" + "12".repeat(1234); - const timestamp = (await getEthTime(ethers.provider)) + 100 - await setEthTime(ethers.provider, timestamp) - await OVM_CanonicalTransactionChain.enqueue(entrypoint, gasLimit, data) + const timestamp = (await getEthTime(ethers.provider)) + 100; + await setEthTime(ethers.provider, timestamp); - const blockNumber = await ethers.provider.getBlockNumber() + await OVM_CanonicalTransactionChain.enqueue( + entrypoint, + gasLimit, + data, + 0, + "0x" + ); + + const blockNumber = await ethers.provider.getBlockNumber(); await appendSequencerBatch( OVM_CanonicalTransactionChain.connect(sequencer), @@ -583,7 +640,7 @@ describe('OVM_CanonicalTransactionChain', () => { ], transactions: [], } - ) + ); expect( await OVM_CanonicalTransactionChain.verifyTransaction( @@ -601,36 +658,45 @@ describe('OVM_CanonicalTransactionChain', () => { queueIndex: 0, timestamp: 0, blockNumber: 0, - txData: '0x', + txData: "0x", }, { batchIndex: 0, batchRoot: getQueueLeafHash(0), batchSize: 1, prevTotalElements: 0, - extraData: '0x', + extraData: "0x", }, { index: 0, siblings: [], } ) - ).to.equal(true) - }) - - it.skip('should successfully verify against a valid queue transaction appended by force', async () => { - const entrypoint = NON_ZERO_ADDRESS - const gasLimit = 500_000 - const data = '0x' + '12'.repeat(1234) - - const timestamp = (await getEthTime(ethers.provider)) + 100 - await setEthTime(ethers.provider, timestamp) - await OVM_CanonicalTransactionChain.enqueue(entrypoint, gasLimit, data) - - const blockNumber = await ethers.provider.getBlockNumber() - await increaseEthTime(ethers.provider, FORCE_INCLUSION_PERIOD_SECONDS * 2) - - await OVM_CanonicalTransactionChain.appendQueueBatch(1) + ).to.equal(true); + }); + + it.skip("should successfully verify against a valid queue transaction appended by force", async () => { + const entrypoint = NON_ZERO_ADDRESS; + const gasLimit = 500_000; + const data = "0x" + "12".repeat(1234); + + const timestamp = (await getEthTime(ethers.provider)) + 100; + await setEthTime(ethers.provider, timestamp); + await OVM_CanonicalTransactionChain.enqueue( + entrypoint, + gasLimit, + data, + 0, + "0x" + ); + + const blockNumber = await ethers.provider.getBlockNumber(); + await increaseEthTime( + ethers.provider, + FORCE_INCLUSION_PERIOD_SECONDS * 2 + ); + + await OVM_CanonicalTransactionChain.appendQueueBatch(1, 0, "0x"); expect( await OVM_CanonicalTransactionChain.verifyTransaction( @@ -648,29 +714,29 @@ describe('OVM_CanonicalTransactionChain', () => { queueIndex: 0, timestamp: 0, blockNumber: 0, - txData: '0x', + txData: "0x", }, { batchIndex: 0, batchRoot: getQueueLeafHash(0), batchSize: 1, prevTotalElements: 0, - extraData: '0x', + extraData: "0x", }, { index: 0, siblings: [], } ) - ).to.equal(true) - }) + ).to.equal(true); + }); - it('should successfully verify against a valid sequencer transaction', async () => { - const entrypoint = DECOMPRESSION_ADDRESS - const gasLimit = MAX_GAS_LIMIT - const data = '0x' + '12'.repeat(1234) - const timestamp = (await getEthTime(ethers.provider)) - 10 - const blockNumber = (await ethers.provider.getBlockNumber()) - 1 + it("should successfully verify against a valid sequencer transaction", async () => { + const entrypoint = DECOMPRESSION_ADDRESS; + const gasLimit = MAX_GAS_LIMIT; + const data = "0x" + "12".repeat(1234); + const timestamp = (await getEthTime(ethers.provider)) - 10; + const blockNumber = (await ethers.provider.getBlockNumber()) - 1; await appendSequencerBatch( OVM_CanonicalTransactionChain.connect(sequencer), @@ -687,7 +753,7 @@ describe('OVM_CanonicalTransactionChain', () => { ], transactions: [data], } - ) + ); expect( await OVM_CanonicalTransactionChain.verifyTransaction( @@ -712,27 +778,27 @@ describe('OVM_CanonicalTransactionChain', () => { batchRoot: getSequencerLeafHash(timestamp, blockNumber, data), batchSize: 1, prevTotalElements: 0, - extraData: '0x', + extraData: "0x", }, { index: 0, siblings: [], } ) - ).to.equal(true) - }) - }) + ).to.equal(true); + }); + }); - describe('appendSequencerBatch', () => { + describe("appendSequencerBatch", () => { beforeEach(() => { OVM_CanonicalTransactionChain = OVM_CanonicalTransactionChain.connect( sequencer - ) - }) + ); + }); - it('should allow for a lower bound per-tx gas usage of <400 gas [GAS BENCHMARK]', async () => { - const timestamp = (await getEthTime(ethers.provider)) - 100 - const blockNumber = await getNextBlockNumber(ethers.provider) + it("should allow for a lower bound per-tx gas usage of <400 gas [GAS BENCHMARK]", async () => { + const timestamp = (await getEthTime(ethers.provider)) - 100; + const blockNumber = await getNextBlockNumber(ethers.provider); // do two batch appends for no reason await appendSequencerBatch(OVM_CanonicalTransactionChain, { @@ -746,8 +812,8 @@ describe('OVM_CanonicalTransactionChain', () => { blockNumber, }, ], - transactions: ['0x1234'], - }) + transactions: ["0x1234"], + }); await appendSequencerBatch(OVM_CanonicalTransactionChain, { shouldStartAtElement: 1, totalElementsToAppend: 1, @@ -759,16 +825,16 @@ describe('OVM_CanonicalTransactionChain', () => { blockNumber, }, ], - transactions: ['0x1234'], - }) + transactions: ["0x1234"], + }); - console.log('\n~~~~ BEGINNGING TRASACTION IN QUESTION ~~~~') - const transactions = [] - const numTxs = 200 + console.log("\n~~~~ BEGINNGING TRASACTION IN QUESTION ~~~~"); + const transactions = []; + const numTxs = 200; for (let i = 0; i < numTxs; i++) { transactions.push( - '0x' + '1080111111111111111111111111111111111111111111'.repeat(20) - ) + "0x" + "1080111111111111111111111111111111111111111111".repeat(20) + ); } const res = await appendSequencerBatch(OVM_CanonicalTransactionChain, { shouldStartAtElement: 2, @@ -782,15 +848,15 @@ describe('OVM_CanonicalTransactionChain', () => { }, ], transactions, - }) - const receipt = await res.wait() - console.log('Benchmark complete. Gas used:', receipt.gasUsed) - }).timeout(100000000) + }); + const receipt = await res.wait(); + console.log("Benchmark complete. Gas used:", receipt.gasUsed); + }).timeout(100000000); - it('should revert if expected start does not match current total batches', async () => { + it("should revert if expected start does not match current total batches", async () => { await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ['0x1234'], + transactions: ["0x1234"], contexts: [ { numSequencedTransactions: 0, @@ -803,17 +869,17 @@ describe('OVM_CanonicalTransactionChain', () => { totalElementsToAppend: 1, }) ).to.be.revertedWith( - 'Actual batch start index does not match expected start index.' - ) - }) + "Actual batch start index does not match expected start index." + ); + }); - it('should revert if not all sequencer transactions are processed', async () => { - const timestamp = await getEthTime(ethers.provider) - const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1 + it("should revert if not all sequencer transactions are processed", async () => { + const timestamp = await getEthTime(ethers.provider); + const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1; await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ['0x1234', '0x1234'], + transactions: ["0x1234", "0x1234"], contexts: [ { numSequencedTransactions: 0, @@ -825,13 +891,13 @@ describe('OVM_CanonicalTransactionChain', () => { shouldStartAtElement: 0, totalElementsToAppend: 1, }) - ).to.be.revertedWith('Not all sequencer transactions were processed.') - }) + ).to.be.revertedWith("Not all sequencer transactions were processed."); + }); - it('should revert if not called by the sequencer', async () => { + it("should revert if not called by the sequencer", async () => { await expect( appendSequencerBatch(OVM_CanonicalTransactionChain.connect(signer), { - transactions: ['0x1234'], + transactions: ["0x1234"], contexts: [ { numSequencedTransactions: 0, @@ -843,24 +909,24 @@ describe('OVM_CanonicalTransactionChain', () => { shouldStartAtElement: 0, totalElementsToAppend: 1, }) - ).to.be.revertedWith('Function can only be called by the Sequencer.') - }) + ).to.be.revertedWith("Function can only be called by the Sequencer."); + }); - it('should revert if no contexts are provided', async () => { + it("should revert if no contexts are provided", async () => { await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ['0x1234'], + transactions: ["0x1234"], contexts: [], shouldStartAtElement: 0, totalElementsToAppend: 1, }) - ).to.be.revertedWith('Must provide at least one batch context.') - }) + ).to.be.revertedWith("Must provide at least one batch context."); + }); - it('should revert if total elements to append is zero', async () => { + it("should revert if total elements to append is zero", async () => { await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ['0x1234'], + transactions: ["0x1234"], contexts: [ { numSequencedTransactions: 0, @@ -872,22 +938,22 @@ describe('OVM_CanonicalTransactionChain', () => { shouldStartAtElement: 0, totalElementsToAppend: 0, }) - ).to.be.revertedWith('Must append at least one element.') - }) + ).to.be.revertedWith("Must append at least one element."); + }); - describe('Sad path cases', () => { - const target = NON_ZERO_ADDRESS - const gasLimit = 500_000 - const data = '0x' + '12'.repeat(1234) + describe("Sad path cases", () => { + const target = NON_ZERO_ADDRESS; + const gasLimit = 500_000; + const data = "0x" + "12".repeat(1234); - describe('when the sequencer attempts to add more queue transactions than exist', () => { - it('reverts when there are zero transactions in the queue', async () => { - const timestamp = await getEthTime(ethers.provider) - const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1 + describe("when the sequencer attempts to add more queue transactions than exist", () => { + it("reverts when there are zero transactions in the queue", async () => { + const timestamp = await getEthTime(ethers.provider); + const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1; await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ['0x1234'], + transactions: ["0x1234"], contexts: [ { numSequencedTransactions: 1, @@ -899,21 +965,27 @@ describe('OVM_CanonicalTransactionChain', () => { shouldStartAtElement: 0, totalElementsToAppend: 1, }) - ).to.be.revertedWith('Not enough queued transactions to append.') - }) + ).to.be.revertedWith("Not enough queued transactions to append."); + }); - it('reverts when there are insufficient (but nonzero) transactions in the queue', async () => { - const timestamp = await getEthTime(ethers.provider) - const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1 + it("reverts when there are insufficient (but nonzero) transactions in the queue", async () => { + const timestamp = await getEthTime(ethers.provider); + const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1; - const numEnqueues = 7 + const numEnqueues = 7; for (let i = 0; i < numEnqueues; i++) { - await OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data) + await OVM_CanonicalTransactionChain.enqueue( + target, + gasLimit, + data, + 0, + "0x" + ); } await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ['0x1234'], + transactions: ["0x1234"], contexts: [ { numSequencedTransactions: 1, @@ -925,19 +997,19 @@ describe('OVM_CanonicalTransactionChain', () => { shouldStartAtElement: 0, totalElementsToAppend: numEnqueues + 1, }) - ).to.be.revertedWith('Not enough queued transactions to append.') - }) - }) + ).to.be.revertedWith("Not enough queued transactions to append."); + }); + }); - describe('when the sequencer attempts to add transactions which are not monotonically increasing', () => { - describe('when the sequencer transactions themselves have out-of-order times', () => { - it('should revert when adding two out-of-order-timestamp sequencer elements', async () => { - const timestamp = await getEthTime(ethers.provider) - const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1 + describe("when the sequencer attempts to add transactions which are not monotonically increasing", () => { + describe("when the sequencer transactions themselves have out-of-order times", () => { + it("should revert when adding two out-of-order-timestamp sequencer elements", async () => { + const timestamp = await getEthTime(ethers.provider); + const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1; await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ['0x1234', '0x5678'], + transactions: ["0x1234", "0x5678"], contexts: [ { numSequencedTransactions: 1, @@ -956,17 +1028,17 @@ describe('OVM_CanonicalTransactionChain', () => { totalElementsToAppend: 2, }) ).to.be.revertedWith( - 'Context timestamp values must monotonically increase.' - ) - }) + "Context timestamp values must monotonically increase." + ); + }); - it('should revert when adding two out-of-order-blocknumber sequencer elements', async () => { - const timestamp = await getEthTime(ethers.provider) - const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1 + it("should revert when adding two out-of-order-blocknumber sequencer elements", async () => { + const timestamp = await getEthTime(ethers.provider); + const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1; await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ['0x1234', '0x5678'], + transactions: ["0x1234", "0x5678"], contexts: [ { numSequencedTransactions: 1, @@ -985,28 +1057,30 @@ describe('OVM_CanonicalTransactionChain', () => { totalElementsToAppend: 2, }) ).to.be.revertedWith( - 'Context blockNumber values must monotonically increase.' - ) - }) - }) - describe('when the elements are out-of-order with regards to pending queue elements', async () => { - describe('adding a single sequencer transaction with a single pending queue element', () => { + "Context blockNumber values must monotonically increase." + ); + }); + }); + describe("when the elements are out-of-order with regards to pending queue elements", async () => { + describe("adding a single sequencer transaction with a single pending queue element", () => { beforeEach(async () => { // enqueue a single element so that it is pending, but do not yet apply it await OVM_CanonicalTransactionChain.enqueue( target, gasLimit, - data - ) - }) - it('should revert if the first context timestamp is > the head queue element timestamp', async () => { - const timestamp = (await getEthTime(ethers.provider)) + 100 + data, + 0, + "0x" + ); + }); + it("should revert if the first context timestamp is > the head queue element timestamp", async () => { + const timestamp = (await getEthTime(ethers.provider)) + 100; const blockNumber = - (await getNextBlockNumber(ethers.provider)) - 1 + (await getNextBlockNumber(ethers.provider)) - 1; await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ['0x1234'], + transactions: ["0x1234"], contexts: [ { numSequencedTransactions: 1, @@ -1019,18 +1093,18 @@ describe('OVM_CanonicalTransactionChain', () => { totalElementsToAppend: 1, }) ).to.be.revertedWith( - 'Sequencer transaction timestamp exceeds that of next queue element.' - ) - }) + "Sequencer transaction timestamp exceeds that of next queue element." + ); + }); - it('should revert if the context block number is > the head queue element block number', async () => { - const timestamp = (await getEthTime(ethers.provider)) - 100 + it("should revert if the context block number is > the head queue element block number", async () => { + const timestamp = (await getEthTime(ethers.provider)) - 100; const blockNumber = - (await getNextBlockNumber(ethers.provider)) + 100 + (await getNextBlockNumber(ethers.provider)) + 100; await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ['0x1234'], + transactions: ["0x1234"], contexts: [ { numSequencedTransactions: 1, @@ -1043,91 +1117,93 @@ describe('OVM_CanonicalTransactionChain', () => { totalElementsToAppend: 1, }) ).to.be.revertedWith( - 'Sequencer transaction blockNumber exceeds that of next queue element.' - ) - }) - }) - describe('adding multiple sequencer transactions with multiple pending queue elements', () => { - const numQueuedTransactions = 10 - const queueElements = [] - const validContexts = [] + "Sequencer transaction blockNumber exceeds that of next queue element." + ); + }); + }); + describe("adding multiple sequencer transactions with multiple pending queue elements", () => { + const numQueuedTransactions = 10; + const queueElements = []; + const validContexts = []; beforeEach(async () => { for (let i = 0; i < numQueuedTransactions; i++) { await OVM_CanonicalTransactionChain.enqueue( target, gasLimit, - data - ) + data, + 0, + "0x" + ); queueElements[ i - ] = await OVM_CanonicalTransactionChain.getQueueElement(i) + ] = await OVM_CanonicalTransactionChain.getQueueElement(i); // this is a valid context for this TX validContexts[i] = { numSequencedTransactions: 1, numSubsequentQueueTransactions: 1, timestamp: queueElements[i].timestamp, blockNumber: queueElements[i].blockNumber, - } + }; } - }) + }); - it('does not revert for valid context', async () => { + it("does not revert for valid context", async () => { await appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: new Array(numQueuedTransactions).fill('0x1234'), + transactions: new Array(numQueuedTransactions).fill("0x1234"), contexts: validContexts, shouldStartAtElement: 0, totalElementsToAppend: 2 * numQueuedTransactions, - }) - }) + }); + }); - it('reverts if wrong timestamp in middle', async () => { - const invalidTimestampContexts = [...validContexts] + it("reverts if wrong timestamp in middle", async () => { + const invalidTimestampContexts = [...validContexts]; // put a bigger timestamp early invalidTimestampContexts[6].timestamp = - invalidTimestampContexts[8].timestamp + invalidTimestampContexts[8].timestamp; await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: new Array(numQueuedTransactions).fill('0x1234'), + transactions: new Array(numQueuedTransactions).fill("0x1234"), contexts: invalidTimestampContexts, shouldStartAtElement: 0, totalElementsToAppend: 2 * numQueuedTransactions, }) ).to.be.revertedWith( - 'Sequencer transaction timestamp exceeds that of next queue element.' - ) - }) + "Sequencer transaction timestamp exceeds that of next queue element." + ); + }); - it('reverts if wrong block number in the middle', async () => { - const invalidBlockNumberContexts = [...validContexts] + it("reverts if wrong block number in the middle", async () => { + const invalidBlockNumberContexts = [...validContexts]; // put a bigger block number early invalidBlockNumberContexts[6].blockNumber = - invalidBlockNumberContexts[8].blockNumber + invalidBlockNumberContexts[8].blockNumber; await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: new Array(numQueuedTransactions).fill('0x1234'), + transactions: new Array(numQueuedTransactions).fill("0x1234"), contexts: invalidBlockNumberContexts, shouldStartAtElement: 0, totalElementsToAppend: 2 * numQueuedTransactions, }) ).to.be.revertedWith( - 'Sequencer transaction blockNumber exceeds that of next queue element.' - ) - }) - }) - }) - }) - - describe('when the sequencer attempts to add transactions with out-of-bounds times', async () => { - describe('when trying to add elements from the future', () => { - it('reverts on initial timestamp in the future', async () => { - const timestamp = (await getEthTime(ethers.provider)) + 100_000_000 - const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1 + "Sequencer transaction blockNumber exceeds that of next queue element." + ); + }); + }); + }); + }); + + describe("when the sequencer attempts to add transactions with out-of-bounds times", async () => { + describe("when trying to add elements from the future", () => { + it("reverts on initial timestamp in the future", async () => { + const timestamp = (await getEthTime(ethers.provider)) + 100_000_000; + const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1; await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ['0x1234'], + transactions: ["0x1234"], contexts: [ { numSequencedTransactions: 1, @@ -1139,16 +1215,16 @@ describe('OVM_CanonicalTransactionChain', () => { shouldStartAtElement: 0, totalElementsToAppend: 1, }) - ).to.be.revertedWith('Context timestamp is from the future.') - }) + ).to.be.revertedWith("Context timestamp is from the future."); + }); - it('reverts on non-initial timestamp in the future', async () => { - const timestamp = await getEthTime(ethers.provider) - const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1 + it("reverts on non-initial timestamp in the future", async () => { + const timestamp = await getEthTime(ethers.provider); + const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1; await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ['0x1234', '0x1234'], + transactions: ["0x1234", "0x1234"], contexts: [ { numSequencedTransactions: 1, @@ -1166,16 +1242,16 @@ describe('OVM_CanonicalTransactionChain', () => { shouldStartAtElement: 0, totalElementsToAppend: 2, }) - ).to.be.revertedWith('Context timestamp is from the future.') - }) + ).to.be.revertedWith("Context timestamp is from the future."); + }); - it('reverts on initial blocknumber in the future', async () => { - const timestamp = await getEthTime(ethers.provider) - const blockNumber = (await getNextBlockNumber(ethers.provider)) + 1 + it("reverts on initial blocknumber in the future", async () => { + const timestamp = await getEthTime(ethers.provider); + const blockNumber = (await getNextBlockNumber(ethers.provider)) + 1; await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ['0x1234'], + transactions: ["0x1234"], contexts: [ { numSequencedTransactions: 1, @@ -1187,23 +1263,23 @@ describe('OVM_CanonicalTransactionChain', () => { shouldStartAtElement: 0, totalElementsToAppend: 1, }) - ).to.be.revertedWith('Context block number is from the future.') - }) - }) - }) - - describe('when trying to add elements which are older than the force inclusion period', async () => { - it('reverts for a timestamp older than the f.i.p. ago', async () => { - const timestamp = await getEthTime(ethers.provider) + ).to.be.revertedWith("Context block number is from the future."); + }); + }); + }); + + describe("when trying to add elements which are older than the force inclusion period", async () => { + it("reverts for a timestamp older than the f.i.p. ago", async () => { + const timestamp = await getEthTime(ethers.provider); await increaseEthTime( ethers.provider, FORCE_INCLUSION_PERIOD_SECONDS + 1 - ) - const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1 + ); + const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1; await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ['0x1234'], + transactions: ["0x1234"], contexts: [ { numSequencedTransactions: 1, @@ -1215,19 +1291,19 @@ describe('OVM_CanonicalTransactionChain', () => { shouldStartAtElement: 0, totalElementsToAppend: 1, }) - ).to.be.revertedWith('Context timestamp too far in the past.') - }) + ).to.be.revertedWith("Context timestamp too far in the past."); + }); - it('reverts for a blockNumber older than the f.i.p. ago', async () => { - const timestamp = await getEthTime(ethers.provider) + it("reverts for a blockNumber older than the f.i.p. ago", async () => { + const timestamp = await getEthTime(ethers.provider); for (let i = 0; i < FORCE_INCLUSION_PERIOD_BLOCKS + 1; i++) { - await mineBlock(ethers.provider) + await mineBlock(ethers.provider); } await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ['0x1234'], + transactions: ["0x1234"], contexts: [ { numSequencedTransactions: 1, @@ -1239,19 +1315,19 @@ describe('OVM_CanonicalTransactionChain', () => { shouldStartAtElement: 0, totalElementsToAppend: 1, }) - ).to.be.revertedWith('Context block number too far in the past.') - }) - }) - - describe('when trying to add elements which are older than already existing CTC elements', () => { - let timestamp - let blockNumber - describe('when the most recent CTC element is a sequencer transaction', () => { + ).to.be.revertedWith("Context block number too far in the past."); + }); + }); + + describe("when trying to add elements which are older than already existing CTC elements", () => { + let timestamp; + let blockNumber; + describe("when the most recent CTC element is a sequencer transaction", () => { beforeEach(async () => { - timestamp = await getEthTime(ethers.provider) - blockNumber = (await getNextBlockNumber(ethers.provider)) - 1 + timestamp = await getEthTime(ethers.provider); + blockNumber = (await getNextBlockNumber(ethers.provider)) - 1; await appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ['0x1234'], + transactions: ["0x1234"], contexts: [ { numSequencedTransactions: 1, @@ -1262,13 +1338,13 @@ describe('OVM_CanonicalTransactionChain', () => { ], shouldStartAtElement: 0, totalElementsToAppend: 1, - }) - }) + }); + }); - it('reverts if timestamp is older than previous one', async () => { + it("reverts if timestamp is older than previous one", async () => { await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ['0x1234'], + transactions: ["0x1234"], contexts: [ { numSequencedTransactions: 1, @@ -1281,14 +1357,14 @@ describe('OVM_CanonicalTransactionChain', () => { totalElementsToAppend: 1, }) ).to.be.revertedWith( - 'Context timestamp is lower than last submitted.' - ) - }) + "Context timestamp is lower than last submitted." + ); + }); - it('reverts if block number is older than previous one', async () => { + it("reverts if block number is older than previous one", async () => { await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ['0x1234'], + transactions: ["0x1234"], contexts: [ { numSequencedTransactions: 1, @@ -1301,19 +1377,25 @@ describe('OVM_CanonicalTransactionChain', () => { totalElementsToAppend: 1, }) ).to.be.revertedWith( - 'Context block number is lower than last submitted.' - ) - }) - }) + "Context block number is lower than last submitted." + ); + }); + }); - describe('when the previous transaction is a queue transaction', () => { + describe("when the previous transaction is a queue transaction", () => { beforeEach(async () => { // enqueue - timestamp = await getEthTime(ethers.provider) - blockNumber = await getNextBlockNumber(ethers.provider) - await OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data) + timestamp = await getEthTime(ethers.provider); + blockNumber = await getNextBlockNumber(ethers.provider); + await OVM_CanonicalTransactionChain.enqueue( + target, + gasLimit, + data, + 0, + "0x" + ); await appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ['0x1234'], + transactions: ["0x1234"], contexts: [ { numSequencedTransactions: 1, @@ -1324,13 +1406,13 @@ describe('OVM_CanonicalTransactionChain', () => { ], shouldStartAtElement: 0, totalElementsToAppend: 2, - }) - }) + }); + }); - it('reverts if timestamp is older than previous one', async () => { + it("reverts if timestamp is older than previous one", async () => { await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ['0x1234'], + transactions: ["0x1234"], contexts: [ { numSequencedTransactions: 1, @@ -1343,14 +1425,14 @@ describe('OVM_CanonicalTransactionChain', () => { totalElementsToAppend: 1, }) ).to.be.revertedWith( - 'Context timestamp is lower than last submitted.' - ) - }) + "Context timestamp is lower than last submitted." + ); + }); - it('reverts if block number is older than previous one', async () => { + it("reverts if block number is older than previous one", async () => { await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ['0x1234'], + transactions: ["0x1234"], contexts: [ { numSequencedTransactions: 1, @@ -1363,28 +1445,34 @@ describe('OVM_CanonicalTransactionChain', () => { totalElementsToAppend: 1, }) ).to.be.revertedWith( - 'Context block number is lower than last submitted.' - ) - }) - }) - }) + "Context block number is lower than last submitted." + ); + }); + }); + }); - it('should revert if a queue element has expired and needs to be included', async () => { + it("should revert if a queue element has expired and needs to be included", async () => { // enqueue a tx - await OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data) + await OVM_CanonicalTransactionChain.enqueue( + target, + gasLimit, + data, + 0, + "0x" + ); // increase time past force inclusion period await increaseEthTime( ethers.provider, FORCE_INCLUSION_PERIOD_SECONDS * 2 - ) + ); - const blockNumber = (await ethers.provider.getBlockNumber()) - 1 + const blockNumber = (await ethers.provider.getBlockNumber()) - 1; - const validTimestamp = (await getBlockTime(ethers.provider)) + 100 + const validTimestamp = (await getBlockTime(ethers.provider)) + 100; await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ['0x1234'], + transactions: ["0x1234"], contexts: [ { numSequencedTransactions: 1, @@ -1397,25 +1485,25 @@ describe('OVM_CanonicalTransactionChain', () => { totalElementsToAppend: 1, }) ).to.be.revertedWith( - 'Previously enqueued batches have expired and must be appended before a new sequencer batch.' - ) - }) - }) + "Previously enqueued batches have expired and must be appended before a new sequencer batch." + ); + }); + }); for (const size of ELEMENT_TEST_SIZES) { - const target = NON_ZERO_ADDRESS - const gasLimit = 500_000 - const data = '0x' + '12'.repeat(1234) + const target = NON_ZERO_ADDRESS; + const gasLimit = 500_000; + const data = "0x" + "12".repeat(1234); describe(`Happy path: when appending ${size} sequencer transactions`, () => { - describe('when not inserting queue elements in between', () => { - describe('when using a single batch context', () => { - let contexts: any[] - let transactions: any[] + describe("when not inserting queue elements in between", () => { + describe("when using a single batch context", () => { + let contexts: any[]; + let transactions: any[]; beforeEach(async () => { - const timestamp = (await getEthTime(ethers.provider)) - 100 + const timestamp = (await getEthTime(ethers.provider)) - 100; const blockNumber = - (await getNextBlockNumber(ethers.provider)) - 10 + (await getNextBlockNumber(ethers.provider)) - 10; contexts = [ { @@ -1424,14 +1512,14 @@ describe('OVM_CanonicalTransactionChain', () => { timestamp, blockNumber, }, - ] + ]; transactions = [...Array(size)].map((el, idx) => { - return '0x' + '12' + '34'.repeat(idx) - }) - }) + return "0x" + "12" + "34".repeat(idx); + }); + }); - it('should append the given number of transactions', async () => { + it("should append the given number of transactions", async () => { await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { transactions, @@ -1442,31 +1530,33 @@ describe('OVM_CanonicalTransactionChain', () => { ) .to.emit( OVM_CanonicalTransactionChain, - 'SequencerBatchAppended' + "SequencerBatchAppended" ) - .withArgs(0, 0, size) - }) - }) - }) + .withArgs(0, 0, size); + }); + }); + }); - describe('when inserting queue elements in between', () => { + describe("when inserting queue elements in between", () => { beforeEach(async () => { for (let i = 0; i < size; i++) { await OVM_CanonicalTransactionChain.enqueue( target, gasLimit, - data - ) + data, + 0, + "0x" + ); } - }) + }); - describe('between every other sequencer transaction', () => { - let contexts: any[] - let transactions: any[] + describe("between every other sequencer transaction", () => { + let contexts: any[]; + let transactions: any[]; beforeEach(async () => { - const timestamp = (await getEthTime(ethers.provider)) - 100 + const timestamp = (await getEthTime(ethers.provider)) - 100; const blockNumber = - (await getNextBlockNumber(ethers.provider)) - 50 + (await getNextBlockNumber(ethers.provider)) - 50; contexts = [...Array(size)].map(() => { return { @@ -1474,15 +1564,15 @@ describe('OVM_CanonicalTransactionChain', () => { numSubsequentQueueTransactions: 1, timestamp, blockNumber: Math.max(blockNumber, 0), - } - }) + }; + }); transactions = [...Array(size)].map((el, idx) => { - return '0x' + '12' + '34'.repeat(idx) - }) - }) + return "0x" + "12" + "34".repeat(idx); + }); + }); - it('should append the batch', async () => { + it("should append the batch", async () => { await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { transactions, @@ -1493,20 +1583,20 @@ describe('OVM_CanonicalTransactionChain', () => { ) .to.emit( OVM_CanonicalTransactionChain, - 'SequencerBatchAppended' + "SequencerBatchAppended" ) - .withArgs(0, size, size * 2) - }) - }) + .withArgs(0, size, size * 2); + }); + }); - const spacing = Math.max(Math.floor(size / 4), 1) + const spacing = Math.max(Math.floor(size / 4), 1); describe(`between every ${spacing} sequencer transaction`, () => { - let contexts: any[] - let transactions: any[] + let contexts: any[]; + let transactions: any[]; beforeEach(async () => { - const timestamp = (await getEthTime(ethers.provider)) - 100 + const timestamp = (await getEthTime(ethers.provider)) - 100; const blockNumber = - (await getNextBlockNumber(ethers.provider)) - 50 + (await getNextBlockNumber(ethers.provider)) - 50; contexts = [...Array(spacing)].map(() => { return { @@ -1514,15 +1604,15 @@ describe('OVM_CanonicalTransactionChain', () => { numSubsequentQueueTransactions: 1, timestamp, blockNumber: Math.max(blockNumber, 0), - } - }) + }; + }); transactions = [...Array(size)].map((el, idx) => { - return '0x' + '12' + '34'.repeat(idx) - }) - }) + return "0x" + "12" + "34".repeat(idx); + }); + }); - it('should append the batch', async () => { + it("should append the batch", async () => { await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { transactions, @@ -1533,26 +1623,28 @@ describe('OVM_CanonicalTransactionChain', () => { ) .to.emit( OVM_CanonicalTransactionChain, - 'SequencerBatchAppended' + "SequencerBatchAppended" ) - .withArgs(0, spacing, size + spacing) - }) - }) - }) - }) + .withArgs(0, spacing, size + spacing); + }); + }); + }); + }); } - }) + }); - describe('getTotalElements', () => { - it('should return zero when no elements exist', async () => { - expect(await OVM_CanonicalTransactionChain.getTotalElements()).to.equal(0) - }) + describe("getTotalElements", () => { + it("should return zero when no elements exist", async () => { + expect(await OVM_CanonicalTransactionChain.getTotalElements()).to.equal( + 0 + ); + }); for (const size of ELEMENT_TEST_SIZES) { describe(`when the sequencer inserts a batch of ${size} elements`, () => { beforeEach(async () => { - const timestamp = (await getEthTime(ethers.provider)) - 100 - const blockNumber = (await getNextBlockNumber(ethers.provider)) - 10 + const timestamp = (await getEthTime(ethers.provider)) - 100; + const blockNumber = (await getNextBlockNumber(ethers.provider)) - 10; const contexts = [ { @@ -1561,11 +1653,11 @@ describe('OVM_CanonicalTransactionChain', () => { timestamp, blockNumber: Math.max(blockNumber, 0), }, - ] + ]; const transactions = [...Array(size)].map((el, idx) => { - return '0x' + '12' + '34'.repeat(idx) - }) + return "0x" + "12" + "34".repeat(idx); + }); const res = await appendSequencerBatch( OVM_CanonicalTransactionChain.connect(sequencer), @@ -1575,16 +1667,16 @@ describe('OVM_CanonicalTransactionChain', () => { shouldStartAtElement: 0, totalElementsToAppend: size, } - ) - await res.wait() - }) + ); + await res.wait(); + }); it(`should return ${size}`, async () => { expect( await OVM_CanonicalTransactionChain.getTotalElements() - ).to.equal(size) - }) - }) + ).to.equal(size); + }); + }); } - }) -}) + }); +}); diff --git a/test/contracts/OVM/chain/OVM_StateCommitmentChain.spec.ts b/test/contracts/OVM/chain/OVM_StateCommitmentChain.spec.ts index 7e4a26264..795ad6ba0 100644 --- a/test/contracts/OVM/chain/OVM_StateCommitmentChain.spec.ts +++ b/test/contracts/OVM/chain/OVM_StateCommitmentChain.spec.ts @@ -1,9 +1,9 @@ -import { expect } from '../../../setup' +import { expect } from "../../../setup"; /* External Imports */ -import { ethers } from 'hardhat' -import { Signer, ContractFactory, Contract } from 'ethers' -import { smockit, MockContract } from '@eth-optimism/smock' +import { ethers } from "hardhat"; +import { Signer, ContractFactory, Contract } from "ethers"; +import { smockit, MockContract } from "@eth-optimism/smock"; /* Internal Imports */ import { @@ -14,358 +14,380 @@ import { getEthTime, NULL_BYTES32, increaseEthTime, -} from '../../../helpers' +} from "../../../helpers"; -describe('OVM_StateCommitmentChain', () => { - let sequencer: Signer - let user: Signer +describe("OVM_StateCommitmentChain", () => { + let sequencer: Signer; + let user: Signer; before(async () => { - ;[sequencer, user] = await ethers.getSigners() - }) + [sequencer, user] = await ethers.getSigners(); + }); - let AddressManager: Contract + let AddressManager: Contract; before(async () => { - AddressManager = await makeAddressManager() - }) + AddressManager = await makeAddressManager(); + }); - let Mock__OVM_CanonicalTransactionChain: MockContract - let Mock__OVM_BondManager: MockContract + let Mock__OVM_CanonicalTransactionChain: MockContract; + let Mock__OVM_BondManager: MockContract; before(async () => { Mock__OVM_CanonicalTransactionChain = await smockit( - await ethers.getContractFactory('OVM_CanonicalTransactionChain') - ) + await ethers.getContractFactory("OVM_CanonicalTransactionChain") + ); await setProxyTarget( AddressManager, - 'OVM_CanonicalTransactionChain', + "OVM_CanonicalTransactionChain", Mock__OVM_CanonicalTransactionChain - ) + ); Mock__OVM_BondManager = await smockit( - await ethers.getContractFactory('OVM_BondManager') - ) + await ethers.getContractFactory("OVM_BondManager") + ); await setProxyTarget( AddressManager, - 'OVM_BondManager', + "OVM_BondManager", Mock__OVM_BondManager - ) + ); - Mock__OVM_BondManager.smocked.isCollateralized.will.return.with(true) + Mock__OVM_BondManager.smocked.isCollateralized.will.return.with(true); await AddressManager.setAddress( - 'OVM_Sequencer', + "OVM_Sequencer", await sequencer.getAddress() - ) - }) + ); + }); - let Factory__OVM_StateCommitmentChain: ContractFactory - let Factory__OVM_ChainStorageContainer: ContractFactory + let Factory__OVM_StateCommitmentChain: ContractFactory; + let Factory__OVM_ChainStorageContainer: ContractFactory; before(async () => { Factory__OVM_StateCommitmentChain = await ethers.getContractFactory( - 'OVM_StateCommitmentChain' - ) + "OVM_StateCommitmentChain" + ); Factory__OVM_ChainStorageContainer = await ethers.getContractFactory( - 'OVM_ChainStorageContainer' - ) - }) + "OVM_ChainStorageContainer" + ); + }); - let OVM_StateCommitmentChain: Contract + let OVM_StateCommitmentChain: Contract; beforeEach(async () => { OVM_StateCommitmentChain = await Factory__OVM_StateCommitmentChain.deploy( AddressManager.address, 60 * 60 * 24 * 7, // 1 week fraud proof window 60 * 30 // 30 minute sequencer publish window - ) + ); const batches = await Factory__OVM_ChainStorageContainer.deploy( AddressManager.address, - 'OVM_StateCommitmentChain' - ) + "OVM_StateCommitmentChain" + ); await AddressManager.setAddress( - 'OVM_ChainStorageContainer:SCC:batches', + "OVM_ChainStorageContainer:SCC:batches", batches.address - ) + ); await AddressManager.setAddress( - 'OVM_StateCommitmentChain', + "OVM_StateCommitmentChain", OVM_StateCommitmentChain.address - ) - }) + ); + }); - describe('appendStateBatch', () => { - describe('when the provided batch is empty', () => { - const batch = [] + describe("appendStateBatch", () => { + describe("when the provided batch is empty", () => { + const batch = []; - it('should revert', async () => { + it("should revert", async () => { await expect( - OVM_StateCommitmentChain.appendStateBatch(batch, 0) - ).to.be.revertedWith('Cannot submit an empty state batch.') - }) - }) + OVM_StateCommitmentChain.appendStateBatch(batch, 0, 0, "0x") + ).to.be.revertedWith("Cannot submit an empty state batch."); + }); + }); - describe('when the provided batch is not empty', () => { - const batch = [NON_NULL_BYTES32] + describe("when the provided batch is not empty", () => { + const batch = [NON_NULL_BYTES32]; - describe('when start index does not match total elements', () => { - it('should revert', async () => { + describe("when start index does not match total elements", () => { + it("should revert", async () => { await expect( - OVM_StateCommitmentChain.appendStateBatch(batch, 1) + OVM_StateCommitmentChain.appendStateBatch(batch, 1, 0, "0x") ).to.be.revertedWith( - 'Actual batch start index does not match expected start index.' - ) - }) - }) + "Actual batch start index does not match expected start index." + ); + }); + }); - describe('when submitting more elements than present in the OVM_CanonicalTransactionChain', () => { + describe("when submitting more elements than present in the OVM_CanonicalTransactionChain", () => { before(() => { Mock__OVM_CanonicalTransactionChain.smocked.getTotalElements.will.return.with( batch.length - 1 - ) - }) + ); + }); - it('should revert', async () => { + it("should revert", async () => { await expect( - OVM_StateCommitmentChain.appendStateBatch(batch, 0) + OVM_StateCommitmentChain.appendStateBatch(batch, 0, 0, "0x") ).to.be.revertedWith( - 'Number of state roots cannot exceed the number of canonical transactions.' - ) - }) - }) + "Number of state roots cannot exceed the number of canonical transactions." + ); + }); + }); - describe('when not submitting more elements than present in the OVM_CanonicalTransactionChain', () => { + describe("when not submitting more elements than present in the OVM_CanonicalTransactionChain", () => { before(() => { Mock__OVM_CanonicalTransactionChain.smocked.getTotalElements.will.return.with( batch.length - ) - }) + ); + }); - it('should append the state batch', async () => { - await expect(OVM_StateCommitmentChain.appendStateBatch(batch, 0)).to - .not.be.reverted - }) - }) + it("should append the state batch", async () => { + await expect( + OVM_StateCommitmentChain.appendStateBatch(batch, 0, 0, "0x") + ).to.not.be.reverted; + }); + }); - describe('when a sequencer submits ', () => { + describe("when a sequencer submits ", () => { beforeEach(async () => { Mock__OVM_CanonicalTransactionChain.smocked.getTotalElements.will.return.with( batch.length * 2 - ) + ); await OVM_StateCommitmentChain.connect(sequencer).appendStateBatch( batch, - 0 - ) - }) - - describe('when inside sequencer publish window', () => { - it('should revert', async () => { + 0, + 0, + "0x" + ); + }); + + describe("when inside sequencer publish window", () => { + it("should revert", async () => { await expect( - OVM_StateCommitmentChain.connect(user).appendStateBatch(batch, 1) + OVM_StateCommitmentChain.connect(user).appendStateBatch( + batch, + 1, + 0, + "0x" + ) ).to.be.revertedWith( - 'Cannot publish state roots within the sequencer publication window.' - ) - }) - }) + "Cannot publish state roots within the sequencer publication window." + ); + }); + }); - describe('when outside sequencer publish window', () => { + describe("when outside sequencer publish window", () => { beforeEach(async () => { - const SEQUENCER_PUBLISH_WINDOW = await OVM_StateCommitmentChain.SEQUENCER_PUBLISH_WINDOW() + const SEQUENCER_PUBLISH_WINDOW = await OVM_StateCommitmentChain.SEQUENCER_PUBLISH_WINDOW(); await increaseEthTime( ethers.provider, SEQUENCER_PUBLISH_WINDOW.toNumber() + 1 - ) - }) + ); + }); - it('should succeed', async () => { + it("should succeed", async () => { await expect( - OVM_StateCommitmentChain.connect(user).appendStateBatch(batch, 1) - ).to.not.be.reverted - }) - }) - }) - }) - }) - - describe('deleteStateBatch', () => { - const batch = [NON_NULL_BYTES32] + OVM_StateCommitmentChain.connect(user).appendStateBatch( + batch, + 1, + 0, + "0x" + ) + ).to.not.be.reverted; + }); + }); + }); + }); + }); + + describe("deleteStateBatch", () => { + const batch = [NON_NULL_BYTES32]; const batchHeader = { batchIndex: 0, batchRoot: NON_NULL_BYTES32, batchSize: 1, prevTotalElements: 0, extraData: NULL_BYTES32, - } + }; beforeEach(async () => { Mock__OVM_CanonicalTransactionChain.smocked.getTotalElements.will.return.with( batch.length - ) - await OVM_StateCommitmentChain.appendStateBatch(batch, 0) + ); + await OVM_StateCommitmentChain.appendStateBatch(batch, 0, 0, "0x"); batchHeader.extraData = ethers.utils.defaultAbiCoder.encode( - ['uint256', 'address'], + ["uint256", "address"], [await getEthTime(ethers.provider), await sequencer.getAddress()] - ) - }) + ); + }); - describe('when the sender is not the OVM_FraudVerifier', () => { + describe("when the sender is not the OVM_FraudVerifier", () => { before(async () => { - await AddressManager.setAddress('OVM_FraudVerifier', ZERO_ADDRESS) - }) + await AddressManager.setAddress("OVM_FraudVerifier", ZERO_ADDRESS); + }); - it('should revert', async () => { + it("should revert", async () => { await expect( - OVM_StateCommitmentChain.deleteStateBatch(batchHeader) + OVM_StateCommitmentChain.deleteStateBatch(batchHeader, 0, "0x") ).to.be.revertedWith( - 'State batches can only be deleted by the OVM_FraudVerifier.' - ) - }) - }) + "State batches can only be deleted by the OVM_FraudVerifier." + ); + }); + }); - describe('when the sender is the OVM_FraudVerifier', () => { + describe("when the sender is the OVM_FraudVerifier", () => { before(async () => { await AddressManager.setAddress( - 'OVM_FraudVerifier', + "OVM_FraudVerifier", await sequencer.getAddress() - ) - }) + ); + }); - describe('when the provided batch index is greater than the total submitted', () => { - it('should revert', async () => { + describe("when the provided batch index is greater than the total submitted", () => { + it("should revert", async () => { await expect( - OVM_StateCommitmentChain.deleteStateBatch({ - ...batchHeader, - batchIndex: 1, - }) - ).to.be.revertedWith('Index out of bounds.') - }) - }) - - describe('when the provided batch index is not greater than the total submitted', () => { - describe('when the provided batch header is invalid', () => { - it('should revert', async () => { - await expect( - OVM_StateCommitmentChain.deleteStateBatch({ + OVM_StateCommitmentChain.deleteStateBatch( + { ...batchHeader, - extraData: '0x' + '22'.repeat(32), - }) - ).to.be.revertedWith('Invalid batch header.') - }) - }) - - describe('when the provided batch header is valid', () => { - it('should remove the batch and all following batches', async () => { - await expect(OVM_StateCommitmentChain.deleteStateBatch(batchHeader)) - .to.not.be.reverted - }) - }) - }) - }) - }) - - describe('getTotalElements', () => { - describe('when no batch elements have been inserted', () => { - it('should return zero', async () => { - expect(await OVM_StateCommitmentChain.getTotalElements()).to.equal(0) - }) - }) - - describe('when one batch element has been inserted', () => { + batchIndex: 1, + }, + 0, + "0x" + ) + ).to.be.revertedWith("Index out of bounds."); + }); + }); + + describe("when the provided batch index is not greater than the total submitted", () => { + describe("when the provided batch header is invalid", () => { + it("should revert", async () => { + await expect( + OVM_StateCommitmentChain.deleteStateBatch( + { + ...batchHeader, + extraData: "0x" + "22".repeat(32), + }, + 0, + "0x" + ) + ).to.be.revertedWith("Invalid batch header."); + }); + }); + + describe("when the provided batch header is valid", () => { + it("should remove the batch and all following batches", async () => { + await expect( + OVM_StateCommitmentChain.deleteStateBatch(batchHeader, 0, "0x") + ).to.not.be.reverted; + }); + }); + }); + }); + }); + + describe("getTotalElements", () => { + describe("when no batch elements have been inserted", () => { + it("should return zero", async () => { + expect(await OVM_StateCommitmentChain.getTotalElements()).to.equal(0); + }); + }); + + describe("when one batch element has been inserted", () => { beforeEach(async () => { - const batch = [NON_NULL_BYTES32] + const batch = [NON_NULL_BYTES32]; Mock__OVM_CanonicalTransactionChain.smocked.getTotalElements.will.return.with( batch.length - ) - await OVM_StateCommitmentChain.appendStateBatch(batch, 0) - }) + ); + await OVM_StateCommitmentChain.appendStateBatch(batch, 0, 0, "0x"); + }); - it('should return the number of inserted batch elements', async () => { - expect(await OVM_StateCommitmentChain.getTotalElements()).to.equal(1) - }) - }) + it("should return the number of inserted batch elements", async () => { + expect(await OVM_StateCommitmentChain.getTotalElements()).to.equal(1); + }); + }); - describe('when 64 batch elements have been inserted in one batch', () => { + describe("when 64 batch elements have been inserted in one batch", () => { beforeEach(async () => { - const batch = Array(64).fill(NON_NULL_BYTES32) + const batch = Array(64).fill(NON_NULL_BYTES32); Mock__OVM_CanonicalTransactionChain.smocked.getTotalElements.will.return.with( batch.length - ) - await OVM_StateCommitmentChain.appendStateBatch(batch, 0) - }) + ); + await OVM_StateCommitmentChain.appendStateBatch(batch, 0, 0, "0x"); + }); - it('should return the number of inserted batch elements', async () => { - expect(await OVM_StateCommitmentChain.getTotalElements()).to.equal(64) - }) - }) + it("should return the number of inserted batch elements", async () => { + expect(await OVM_StateCommitmentChain.getTotalElements()).to.equal(64); + }); + }); - describe('when 32 batch elements have been inserted in each of two batches', () => { + describe("when 32 batch elements have been inserted in each of two batches", () => { beforeEach(async () => { - const batch = Array(32).fill(NON_NULL_BYTES32) + const batch = Array(32).fill(NON_NULL_BYTES32); Mock__OVM_CanonicalTransactionChain.smocked.getTotalElements.will.return.with( batch.length * 2 - ) - await OVM_StateCommitmentChain.appendStateBatch(batch, 0) - await OVM_StateCommitmentChain.appendStateBatch(batch, 32) - }) - - it('should return the number of inserted batch elements', async () => { - expect(await OVM_StateCommitmentChain.getTotalElements()).to.equal(64) - }) - }) - }) - - describe('getTotalBatches()', () => { - describe('when no batches have been inserted', () => { - it('should return zero', async () => { - expect(await OVM_StateCommitmentChain.getTotalBatches()).to.equal(0) - }) - }) - - describe('when one batch has been inserted', () => { + ); + await OVM_StateCommitmentChain.appendStateBatch(batch, 0, 0, "0x"); + await OVM_StateCommitmentChain.appendStateBatch(batch, 32, 0, "0x"); + }); + + it("should return the number of inserted batch elements", async () => { + expect(await OVM_StateCommitmentChain.getTotalElements()).to.equal(64); + }); + }); + }); + + describe("getTotalBatches()", () => { + describe("when no batches have been inserted", () => { + it("should return zero", async () => { + expect(await OVM_StateCommitmentChain.getTotalBatches()).to.equal(0); + }); + }); + + describe("when one batch has been inserted", () => { beforeEach(async () => { - const batch = [NON_NULL_BYTES32] + const batch = [NON_NULL_BYTES32]; Mock__OVM_CanonicalTransactionChain.smocked.getTotalElements.will.return.with( batch.length - ) - await OVM_StateCommitmentChain.appendStateBatch(batch, 0) - }) + ); + await OVM_StateCommitmentChain.appendStateBatch(batch, 0, 0, "0x"); + }); - it('should return the number of inserted batch elements', async () => { - expect(await OVM_StateCommitmentChain.getTotalBatches()).to.equal(1) - }) - }) + it("should return the number of inserted batch elements", async () => { + expect(await OVM_StateCommitmentChain.getTotalBatches()).to.equal(1); + }); + }); - describe('when 8 batches have been inserted', () => { + describe("when 8 batches have been inserted", () => { beforeEach(async () => { - const batch = [NON_NULL_BYTES32] + const batch = [NON_NULL_BYTES32]; Mock__OVM_CanonicalTransactionChain.smocked.getTotalElements.will.return.with( batch.length * 8 - ) + ); for (let i = 0; i < 8; i++) { - await OVM_StateCommitmentChain.appendStateBatch(batch, i) + await OVM_StateCommitmentChain.appendStateBatch(batch, i, 0, "0x"); } - }) + }); - it('should return the number of inserted batch elements', async () => { - expect(await OVM_StateCommitmentChain.getTotalBatches()).to.equal(8) - }) - }) - }) + it("should return the number of inserted batch elements", async () => { + expect(await OVM_StateCommitmentChain.getTotalBatches()).to.equal(8); + }); + }); + }); - describe('verifyElement()', () => { - it('should revert when given an invalid batch header', async () => { + describe("verifyElement()", () => { + it("should revert when given an invalid batch header", async () => { // TODO - }) + }); - it('should revert when given an invalid inclusion proof', async () => { + it("should revert when given an invalid inclusion proof", async () => { // TODO - }) + }); - it('should return true when given a valid proof', async () => { + it("should return true when given a valid proof", async () => { // TODO - }) - }) -}) + }); + }); +}); diff --git a/test/contracts/OVM/verification/OVM_FraudVerifier.spec.ts b/test/contracts/OVM/verification/OVM_FraudVerifier.spec.ts index 8d8bc8c6c..9ad9a043b 100644 --- a/test/contracts/OVM/verification/OVM_FraudVerifier.spec.ts +++ b/test/contracts/OVM/verification/OVM_FraudVerifier.spec.ts @@ -1,9 +1,9 @@ -import { expect } from '../../../setup' +import { expect } from "../../../setup"; /* External Imports */ -import { ethers } from 'hardhat' -import { ContractFactory, Contract, BigNumber } from 'ethers' -import { smockit, MockContract } from '@eth-optimism/smock' +import { ethers } from "hardhat"; +import { ContractFactory, Contract, BigNumber } from "ethers"; +import { smockit, MockContract } from "@eth-optimism/smock"; /* Internal Imports */ import { @@ -15,7 +15,7 @@ import { NON_NULL_BYTES32, NULL_BYTES32, hashTransaction, -} from '../../../helpers' +} from "../../../helpers"; const DUMMY_TX_CHAIN_ELEMENTS = [...Array(10).keys()].map((i) => { return { @@ -24,102 +24,102 @@ const DUMMY_TX_CHAIN_ELEMENTS = [...Array(10).keys()].map((i) => { timestamp: BigNumber.from(i), blockNumber: BigNumber.from(0), txData: NULL_BYTES32, - } -}) + }; +}); -const DUMMY_HASH = hashTransaction(DUMMY_OVM_TRANSACTIONS[0]) +const DUMMY_HASH = hashTransaction(DUMMY_OVM_TRANSACTIONS[0]); const DUMMY_BATCH_PROOFS_WITH_INDEX = [ { index: 11, siblings: [NULL_BYTES32], }, -] +]; -describe('OVM_FraudVerifier', () => { - let AddressManager: Contract +describe("OVM_FraudVerifier", () => { + let AddressManager: Contract; before(async () => { - AddressManager = await makeAddressManager() - }) - - let Mock__OVM_StateCommitmentChain: MockContract - let Mock__OVM_CanonicalTransactionChain: MockContract - let Mock__OVM_StateTransitioner: MockContract - let Mock__OVM_StateTransitionerFactory: MockContract - let Mock__OVM_BondManager: MockContract + AddressManager = await makeAddressManager(); + }); + + let Mock__OVM_StateCommitmentChain: MockContract; + let Mock__OVM_CanonicalTransactionChain: MockContract; + let Mock__OVM_StateTransitioner: MockContract; + let Mock__OVM_StateTransitionerFactory: MockContract; + let Mock__OVM_BondManager: MockContract; before(async () => { Mock__OVM_StateCommitmentChain = await smockit( - await ethers.getContractFactory('OVM_StateCommitmentChain') - ) + await ethers.getContractFactory("OVM_StateCommitmentChain") + ); Mock__OVM_CanonicalTransactionChain = await smockit( - await ethers.getContractFactory('OVM_CanonicalTransactionChain') - ) + await ethers.getContractFactory("OVM_CanonicalTransactionChain") + ); Mock__OVM_StateTransitioner = await smockit( - await ethers.getContractFactory('OVM_StateTransitioner') - ) + await ethers.getContractFactory("OVM_StateTransitioner") + ); Mock__OVM_StateTransitionerFactory = await smockit( - await ethers.getContractFactory('OVM_StateTransitionerFactory') - ) + await ethers.getContractFactory("OVM_StateTransitionerFactory") + ); Mock__OVM_BondManager = await smockit( - await ethers.getContractFactory('OVM_BondManager') - ) + await ethers.getContractFactory("OVM_BondManager") + ); await setProxyTarget( AddressManager, - 'OVM_StateCommitmentChain', + "OVM_StateCommitmentChain", Mock__OVM_StateCommitmentChain - ) + ); await setProxyTarget( AddressManager, - 'OVM_CanonicalTransactionChain', + "OVM_CanonicalTransactionChain", Mock__OVM_CanonicalTransactionChain - ) + ); await setProxyTarget( AddressManager, - 'OVM_StateTransitionerFactory', + "OVM_StateTransitionerFactory", Mock__OVM_StateTransitionerFactory - ) + ); await setProxyTarget( AddressManager, - 'OVM_BondManager', + "OVM_BondManager", Mock__OVM_BondManager - ) + ); Mock__OVM_StateTransitionerFactory.smocked.create.will.return.with( Mock__OVM_StateTransitioner.address - ) - }) + ); + }); - let Factory__OVM_FraudVerifier: ContractFactory + let Factory__OVM_FraudVerifier: ContractFactory; before(async () => { Factory__OVM_FraudVerifier = await ethers.getContractFactory( - 'OVM_FraudVerifier' - ) - }) + "OVM_FraudVerifier" + ); + }); - let OVM_FraudVerifier: Contract + let OVM_FraudVerifier: Contract; beforeEach(async () => { OVM_FraudVerifier = await Factory__OVM_FraudVerifier.deploy( AddressManager.address - ) - }) + ); + }); - describe('initializeFraudVerification', () => { - describe('when provided an invalid pre-state root inclusion proof', () => { + describe("initializeFraudVerification", () => { + describe("when provided an invalid pre-state root inclusion proof", () => { before(() => { Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with( false - ) - }) + ); + }); - it('should revert', async () => { + it("should revert", async () => { await expect( OVM_FraudVerifier.initializeFraudVerification( NULL_BYTES32, @@ -130,25 +130,25 @@ describe('OVM_FraudVerifier', () => { DUMMY_BATCH_HEADERS[0], DUMMY_BATCH_PROOFS[0] ) - ).to.be.revertedWith('Invalid pre-state root inclusion proof.') - }) - }) + ).to.be.revertedWith("Invalid pre-state root inclusion proof."); + }); + }); - describe('when provided a valid pre-state root inclusion proof', () => { + describe("when provided a valid pre-state root inclusion proof", () => { before(() => { Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with( true - ) - }) + ); + }); - describe('when provided an invalid transaction inclusion proof', () => { + describe("when provided an invalid transaction inclusion proof", () => { before(() => { Mock__OVM_CanonicalTransactionChain.smocked.verifyTransaction.will.return.with( false - ) - }) + ); + }); - it('should revert', async () => { + it("should revert", async () => { await expect( OVM_FraudVerifier.initializeFraudVerification( NULL_BYTES32, @@ -159,18 +159,18 @@ describe('OVM_FraudVerifier', () => { DUMMY_BATCH_HEADERS[0], DUMMY_BATCH_PROOFS[0] ) - ).to.be.revertedWith('Invalid transaction inclusion proof.') - }) - }) + ).to.be.revertedWith("Invalid transaction inclusion proof."); + }); + }); - describe('when provided a valid transaction inclusion proof', () => { + describe("when provided a valid transaction inclusion proof", () => { before(() => { Mock__OVM_CanonicalTransactionChain.smocked.verifyTransaction.will.return.with( true - ) - }) + ); + }); - it('should deploy a new state transitioner', async () => { + it("should deploy a new state transitioner", async () => { await expect( OVM_FraudVerifier.initializeFraudVerification( NULL_BYTES32, @@ -184,17 +184,17 @@ describe('OVM_FraudVerifier', () => { index: DUMMY_BATCH_PROOFS[0].index + 1, } ) - ).to.not.be.reverted + ).to.not.be.reverted; expect( await OVM_FraudVerifier.getStateTransitioner( NULL_BYTES32, DUMMY_HASH ) - ).to.equal(Mock__OVM_StateTransitioner.address) - }) + ).to.equal(Mock__OVM_StateTransitioner.address); + }); - it('should revert when provided with a incorrect transaction root global index', async () => { + it("should revert when provided with a incorrect transaction root global index", async () => { await expect( OVM_FraudVerifier.initializeFraudVerification( NULL_BYTES32, @@ -206,21 +206,21 @@ describe('OVM_FraudVerifier', () => { DUMMY_BATCH_PROOFS_WITH_INDEX[0] ) ).to.be.revertedWith( - 'Pre-state root global index must equal to the transaction root global index.' - ) - }) - }) - }) - }) - - describe('finalizeFraudVerification', () => { + "Pre-state root global index must equal to the transaction root global index." + ); + }); + }); + }); + }); + + describe("finalizeFraudVerification", () => { beforeEach(async () => { Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with( true - ) + ); Mock__OVM_CanonicalTransactionChain.smocked.verifyTransaction.will.return.with( true - ) + ); await OVM_FraudVerifier.initializeFraudVerification( NULL_BYTES32, @@ -233,15 +233,15 @@ describe('OVM_FraudVerifier', () => { ...DUMMY_BATCH_PROOFS[0], index: DUMMY_BATCH_PROOFS[0].index + 1, } - ) - }) + ); + }); - describe('when the transition process is not complete', () => { + describe("when the transition process is not complete", () => { before(async () => { - Mock__OVM_StateTransitioner.smocked.isComplete.will.return.with(false) - }) + Mock__OVM_StateTransitioner.smocked.isComplete.will.return.with(false); + }); - it('should revert', async () => { + it("should revert", async () => { await expect( OVM_FraudVerifier.finalizeFraudVerification( NULL_BYTES32, @@ -253,23 +253,23 @@ describe('OVM_FraudVerifier', () => { DUMMY_BATCH_PROOFS[0] ) ).to.be.revertedWith( - 'State transition process must be completed prior to finalization.' - ) - }) - }) + "State transition process must be completed prior to finalization." + ); + }); + }); - describe('when the transition process is complete', () => { + describe("when the transition process is complete", () => { before(() => { - Mock__OVM_StateTransitioner.smocked.isComplete.will.return.with(true) - }) + Mock__OVM_StateTransitioner.smocked.isComplete.will.return.with(true); + }); - describe('when provided an invalid post-state root index', () => { + describe("when provided an invalid post-state root index", () => { const batchProof = { ...DUMMY_BATCH_PROOFS[0], index: DUMMY_BATCH_PROOFS[0].index + 2, - } + }; - it('should revert', async () => { + it("should revert", async () => { await expect( OVM_FraudVerifier.finalizeFraudVerification( NULL_BYTES32, @@ -281,25 +281,25 @@ describe('OVM_FraudVerifier', () => { batchProof ) ).to.be.revertedWith( - 'Post-state root global index must equal to the pre state root global index plus one.' - ) - }) - }) + "Post-state root global index must equal to the pre state root global index plus one." + ); + }); + }); - describe('when provided a valid post-state root index', () => { + describe("when provided a valid post-state root index", () => { const batchProof = { ...DUMMY_BATCH_PROOFS[0], index: DUMMY_BATCH_PROOFS[0].index + 1, - } + }; - describe('when provided an invalid pre-state root inclusion proof', () => { + describe("when provided an invalid pre-state root inclusion proof", () => { beforeEach(() => { Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with( false - ) - }) + ); + }); - it('should revert', async () => { + it("should revert", async () => { await expect( OVM_FraudVerifier.finalizeFraudVerification( NULL_BYTES32, @@ -310,27 +310,27 @@ describe('OVM_FraudVerifier', () => { DUMMY_BATCH_HEADERS[0], batchProof ) - ).to.be.revertedWith('Invalid pre-state root inclusion proof.') - }) - }) + ).to.be.revertedWith("Invalid pre-state root inclusion proof."); + }); + }); - describe('when provided a valid pre-state root inclusion proof', () => { + describe("when provided a valid pre-state root inclusion proof", () => { before(() => { Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with( true - ) - }) + ); + }); - describe('when provided an invalid post-state root inclusion proof', () => { + describe("when provided an invalid post-state root inclusion proof", () => { beforeEach(() => { Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with( (stateRoot: string, ...args: any) => { - return stateRoot !== NON_NULL_BYTES32 + return stateRoot !== NON_NULL_BYTES32; } - ) - }) + ); + }); - it('should revert', async () => { + it("should revert", async () => { await expect( OVM_FraudVerifier.finalizeFraudVerification( NULL_BYTES32, @@ -341,25 +341,25 @@ describe('OVM_FraudVerifier', () => { DUMMY_BATCH_HEADERS[0], batchProof ) - ).to.be.revertedWith('Invalid post-state root inclusion proof.') - }) - }) + ).to.be.revertedWith("Invalid post-state root inclusion proof."); + }); + }); - describe('when provided a valid post-state root inclusion proof', () => { + describe("when provided a valid post-state root inclusion proof", () => { before(() => { Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with( true - ) - }) + ); + }); - describe('when the provided post-state root does not differ from the computed one', () => { + describe("when the provided post-state root does not differ from the computed one", () => { before(() => { Mock__OVM_StateTransitioner.smocked.getPostStateRoot.will.return.with( NON_NULL_BYTES32 - ) - }) + ); + }); - it('should revert', async () => { + it("should revert", async () => { await expect( OVM_FraudVerifier.finalizeFraudVerification( NULL_BYTES32, @@ -371,19 +371,19 @@ describe('OVM_FraudVerifier', () => { batchProof ) ).to.be.revertedWith( - 'State transition has not been proven fraudulent.' - ) - }) - }) + "State transition has not been proven fraudulent." + ); + }); + }); - describe('when the provided post-state root differs from the computed one', () => { + describe("when the provided post-state root differs from the computed one", () => { before(() => { Mock__OVM_StateTransitioner.smocked.getPostStateRoot.will.return.with( NULL_BYTES32 - ) - }) + ); + }); - it('should succeed and attempt to delete a state batch', async () => { + it("should succeed and attempt to delete a state batch", async () => { await OVM_FraudVerifier.finalizeFraudVerification( NULL_BYTES32, DUMMY_BATCH_HEADERS[0], @@ -392,44 +392,46 @@ describe('OVM_FraudVerifier', () => { NON_NULL_BYTES32, DUMMY_BATCH_HEADERS[0], batchProof - ) + ); + + const batchHeader = Object.values(DUMMY_BATCH_HEADERS[0]).map( + (value) => { + return Number.isInteger(value) + ? BigNumber.from(value) + : value; + } + ); expect( Mock__OVM_StateCommitmentChain.smocked.deleteStateBatch .calls[0] - ).to.deep.equal([ - Object.values(DUMMY_BATCH_HEADERS[0]).map((value) => { - return Number.isInteger(value) - ? BigNumber.from(value) - : value - }), - ]) - }) - }) - }) - }) - }) - - describe('multiple fraud proofs for the same pre-execution state', () => { - let state2: any - const DUMMY_HASH_2 = hashTransaction(DUMMY_OVM_TRANSACTIONS[1]) + ).to.deep.equal([batchHeader, BigNumber.from("0"), "0x"]); + }); + }); + }); + }); + }); + + describe("multiple fraud proofs for the same pre-execution state", () => { + let state2: any; + const DUMMY_HASH_2 = hashTransaction(DUMMY_OVM_TRANSACTIONS[1]); beforeEach(async () => { state2 = await smockit( - await ethers.getContractFactory('OVM_StateTransitioner') - ) + await ethers.getContractFactory("OVM_StateTransitioner") + ); Mock__OVM_StateTransitionerFactory.smocked.create.will.return.with( state2.address - ) + ); Mock__OVM_StateTransitioner.smocked.getPostStateRoot.will.return.with( NULL_BYTES32 - ) + ); - state2.smocked.getPostStateRoot.will.return.with(NULL_BYTES32) - }) + state2.smocked.getPostStateRoot.will.return.with(NULL_BYTES32); + }); - it('creates multiple state transitioners per tx hash', async () => { + it("creates multiple state transitioners per tx hash", async () => { await expect( OVM_FraudVerifier.initializeFraudVerification( NULL_BYTES32, @@ -443,29 +445,29 @@ describe('OVM_FraudVerifier', () => { index: DUMMY_BATCH_PROOFS[0].index + 1, } ) - ).to.not.be.reverted + ).to.not.be.reverted; expect( await OVM_FraudVerifier.getStateTransitioner( NULL_BYTES32, DUMMY_HASH ) - ).to.equal(Mock__OVM_StateTransitioner.address) + ).to.equal(Mock__OVM_StateTransitioner.address); expect( await OVM_FraudVerifier.getStateTransitioner( NULL_BYTES32, DUMMY_HASH_2 ) - ).to.equal(state2.address) - }) + ).to.equal(state2.address); + }); const batchProof = { ...DUMMY_BATCH_PROOFS[0], index: DUMMY_BATCH_PROOFS[0].index + 1, - } + }; // TODO: Appears to be failing because of a bug in smock. - it.skip('Case 1: allows proving fraud on the same pre-state root twice', async () => { + it.skip("Case 1: allows proving fraud on the same pre-state root twice", async () => { // finalize previous fraud await OVM_FraudVerifier.finalizeFraudVerification( NULL_BYTES32, @@ -475,7 +477,7 @@ describe('OVM_FraudVerifier', () => { NON_NULL_BYTES32, DUMMY_BATCH_HEADERS[0], batchProof - ) + ); // start new fraud await OVM_FraudVerifier.initializeFraudVerification( @@ -489,7 +491,7 @@ describe('OVM_FraudVerifier', () => { ...DUMMY_BATCH_PROOFS[0], index: DUMMY_BATCH_PROOFS[0].index + 1, } - ) + ); // finalize it as well await OVM_FraudVerifier.finalizeFraudVerification( @@ -500,20 +502,20 @@ describe('OVM_FraudVerifier', () => { NON_NULL_BYTES32, DUMMY_BATCH_HEADERS[1], batchProof - ) + ); // the new batch was deleted expect( Mock__OVM_StateCommitmentChain.smocked.deleteStateBatch.calls[0] ).to.deep.equal([ Object.values(DUMMY_BATCH_HEADERS[1]).map((value) => { - return Number.isInteger(value) ? BigNumber.from(value) : value + return Number.isInteger(value) ? BigNumber.from(value) : value; }), - ]) - }) + ]); + }); // TODO: Appears to be failing because of a bug in smock. - it.skip('Case 2: does not get blocked by the first transitioner', async () => { + it.skip("Case 2: does not get blocked by the first transitioner", async () => { // start new fraud await OVM_FraudVerifier.initializeFraudVerification( NULL_BYTES32, @@ -526,7 +528,7 @@ describe('OVM_FraudVerifier', () => { ...DUMMY_BATCH_PROOFS[0], index: DUMMY_BATCH_PROOFS[0].index + 1, } - ) + ); // finalize the new fraud first await OVM_FraudVerifier.finalizeFraudVerification( @@ -537,16 +539,16 @@ describe('OVM_FraudVerifier', () => { NON_NULL_BYTES32, DUMMY_BATCH_HEADERS[1], batchProof - ) + ); // the new fraud's batch was deleted expect( Mock__OVM_StateCommitmentChain.smocked.deleteStateBatch.calls[0] ).to.deep.equal([ Object.values(DUMMY_BATCH_HEADERS[1]).map((value) => { - return Number.isInteger(value) ? BigNumber.from(value) : value + return Number.isInteger(value) ? BigNumber.from(value) : value; }), - ]) + ]); // finalize previous fraud await OVM_FraudVerifier.finalizeFraudVerification( @@ -557,18 +559,18 @@ describe('OVM_FraudVerifier', () => { NON_NULL_BYTES32, DUMMY_BATCH_HEADERS[0], batchProof - ) + ); // the old fraud's batch was deleted expect( Mock__OVM_StateCommitmentChain.smocked.deleteStateBatch.calls[0] ).to.deep.equal([ Object.values(DUMMY_BATCH_HEADERS[0]).map((value) => { - return Number.isInteger(value) ? BigNumber.from(value) : value + return Number.isInteger(value) ? BigNumber.from(value) : value; }), - ]) - }) - }) - }) - }) -}) + ]); + }); + }); + }); + }); +}); From 3eb4ad10e260df97b700f4a28a99af0f6b6c1548 Mon Sep 17 00:00:00 2001 From: Patrick McCorry Date: Mon, 8 Mar 2021 12:23:59 +0000 Subject: [PATCH 2/2] Lint fix --- hardhat.config.ts | 24 +- .../base/OVM_L1CrossDomainMessenger.spec.ts | 284 ++-- .../OVM_CanonicalTransactionChain.spec.ts | 1184 ++++++++--------- .../chain/OVM_StateCommitmentChain.spec.ts | 440 +++--- .../verification/OVM_FraudVerifier.spec.ts | 364 ++--- 5 files changed, 1144 insertions(+), 1152 deletions(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index 162a91d27..9e8188a88 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -1,16 +1,16 @@ -import { HardhatUserConfig } from "hardhat/types"; +import { HardhatUserConfig } from 'hardhat/types' import { DEFAULT_ACCOUNTS_HARDHAT, RUN_OVM_TEST_GAS, -} from "./test/helpers/constants"; +} from './test/helpers/constants' // Hardhat plugins -import "@nomiclabs/hardhat-ethers"; -import "@nomiclabs/hardhat-waffle"; -import "hardhat-typechain"; -import "@eth-optimism/plugins/hardhat/compiler"; -import "@eth-optimism/smock/build/src/plugins/hardhat-storagelayout"; +import '@nomiclabs/hardhat-ethers' +import '@nomiclabs/hardhat-waffle' +import 'hardhat-typechain' +import '@eth-optimism/plugins/hardhat/compiler' +import '@eth-optimism/smock/build/src/plugins/hardhat-storagelayout' const config: HardhatUserConfig = { networks: { @@ -23,15 +23,15 @@ const config: HardhatUserConfig = { timeout: 50000, }, solidity: { - version: "0.7.6", + version: '0.7.6', settings: { optimizer: { enabled: true, runs: 200 }, }, }, typechain: { - outDir: "src/types", - target: "ethers-v5", + outDir: 'src/types', + target: 'ethers-v5', }, -}; +} -export default config; +export default config diff --git a/test/contracts/OVM/bridge/base/OVM_L1CrossDomainMessenger.spec.ts b/test/contracts/OVM/bridge/base/OVM_L1CrossDomainMessenger.spec.ts index 8b0191466..597803c9f 100644 --- a/test/contracts/OVM/bridge/base/OVM_L1CrossDomainMessenger.spec.ts +++ b/test/contracts/OVM/bridge/base/OVM_L1CrossDomainMessenger.spec.ts @@ -1,10 +1,10 @@ -import { expect } from "../../../../setup"; +import { expect } from '../../../../setup' /* External Imports */ -import { ethers } from "hardhat"; -import { Signer, ContractFactory, Contract, BigNumber } from "ethers"; -import { smockit, MockContract } from "@eth-optimism/smock"; -import { remove0x, toHexString } from "@eth-optimism/core-utils"; +import { ethers } from 'hardhat' +import { Signer, ContractFactory, Contract, BigNumber } from 'ethers' +import { smockit, MockContract } from '@eth-optimism/smock' +import { remove0x, toHexString } from '@eth-optimism/core-utils' /* Internal Imports */ import { @@ -18,128 +18,128 @@ import { TrieTestGenerator, getNextBlockNumber, getXDomainCalldata, -} from "../../../../helpers"; -import { keccak256 } from "ethers/lib/utils"; +} from '../../../../helpers' +import { keccak256 } from 'ethers/lib/utils' const deployProxyXDomainMessenger = async ( addressManager: Contract, l1XDomainMessenger: Contract ): Promise => { await addressManager.setAddress( - "OVM_L1CrossDomainMessenger", + 'OVM_L1CrossDomainMessenger', l1XDomainMessenger.address - ); + ) const proxy = await ( - await ethers.getContractFactory("Lib_ResolvedDelegateProxy") - ).deploy(addressManager.address, "OVM_L1CrossDomainMessenger"); - return l1XDomainMessenger.attach(proxy.address); -}; + await ethers.getContractFactory('Lib_ResolvedDelegateProxy') + ).deploy(addressManager.address, 'OVM_L1CrossDomainMessenger') + return l1XDomainMessenger.attach(proxy.address) +} -describe("OVM_L1CrossDomainMessenger", () => { - let signer: Signer; +describe('OVM_L1CrossDomainMessenger', () => { + let signer: Signer before(async () => { - [signer] = await ethers.getSigners(); - }); + ;[signer] = await ethers.getSigners() + }) - let AddressManager: Contract; + let AddressManager: Contract before(async () => { - AddressManager = await makeAddressManager(); - }); + AddressManager = await makeAddressManager() + }) - let Mock__TargetContract: MockContract; - let Mock__OVM_L2CrossDomainMessenger: MockContract; - let Mock__OVM_CanonicalTransactionChain: MockContract; - let Mock__OVM_StateCommitmentChain: MockContract; + let Mock__TargetContract: MockContract + let Mock__OVM_L2CrossDomainMessenger: MockContract + let Mock__OVM_CanonicalTransactionChain: MockContract + let Mock__OVM_StateCommitmentChain: MockContract before(async () => { Mock__TargetContract = await smockit( - await ethers.getContractFactory("Helper_SimpleProxy") - ); + await ethers.getContractFactory('Helper_SimpleProxy') + ) Mock__OVM_L2CrossDomainMessenger = await smockit( - await ethers.getContractFactory("OVM_L2CrossDomainMessenger") - ); + await ethers.getContractFactory('OVM_L2CrossDomainMessenger') + ) Mock__OVM_CanonicalTransactionChain = await smockit( - await ethers.getContractFactory("OVM_CanonicalTransactionChain") - ); + await ethers.getContractFactory('OVM_CanonicalTransactionChain') + ) Mock__OVM_StateCommitmentChain = await smockit( - await ethers.getContractFactory("OVM_StateCommitmentChain") - ); + await ethers.getContractFactory('OVM_StateCommitmentChain') + ) await AddressManager.setAddress( - "OVM_L2CrossDomainMessenger", + 'OVM_L2CrossDomainMessenger', Mock__OVM_L2CrossDomainMessenger.address - ); + ) await setProxyTarget( AddressManager, - "OVM_CanonicalTransactionChain", + 'OVM_CanonicalTransactionChain', Mock__OVM_CanonicalTransactionChain - ); + ) await setProxyTarget( AddressManager, - "OVM_StateCommitmentChain", + 'OVM_StateCommitmentChain', Mock__OVM_StateCommitmentChain - ); - }); + ) + }) - let Factory__OVM_L1CrossDomainMessenger: ContractFactory; + let Factory__OVM_L1CrossDomainMessenger: ContractFactory before(async () => { Factory__OVM_L1CrossDomainMessenger = await ethers.getContractFactory( - "OVM_L1CrossDomainMessenger" - ); - }); + 'OVM_L1CrossDomainMessenger' + ) + }) - let OVM_L1CrossDomainMessenger: Contract; + let OVM_L1CrossDomainMessenger: Contract beforeEach(async () => { - const xDomainMessenerImpl = await Factory__OVM_L1CrossDomainMessenger.deploy(); + const xDomainMessenerImpl = await Factory__OVM_L1CrossDomainMessenger.deploy() // We use an upgradable proxy for the XDomainMessenger--deploy & set up the proxy. OVM_L1CrossDomainMessenger = await deployProxyXDomainMessenger( AddressManager, xDomainMessenerImpl - ); - await OVM_L1CrossDomainMessenger.initialize(AddressManager.address); - }); + ) + await OVM_L1CrossDomainMessenger.initialize(AddressManager.address) + }) - describe("sendMessage", () => { - const target = NON_ZERO_ADDRESS; - const message = NON_NULL_BYTES32; - const gasLimit = 100_000; + describe('sendMessage', () => { + const target = NON_ZERO_ADDRESS + const message = NON_NULL_BYTES32 + const gasLimit = 100_000 - it("should be able to send a single message", async () => { + it('should be able to send a single message', async () => { await expect( OVM_L1CrossDomainMessenger.sendMessage(target, message, gasLimit) - ).to.not.be.reverted; + ).to.not.be.reverted console.log( JSON.stringify( Mock__OVM_CanonicalTransactionChain.smocked.enqueue.calls[0] ) - ); + ) expect( Mock__OVM_CanonicalTransactionChain.smocked.enqueue.calls[0] ).to.deep.equal([ Mock__OVM_L2CrossDomainMessenger.address, BigNumber.from(gasLimit), getXDomainCalldata(await signer.getAddress(), target, message, 0), - BigNumber.from("0"), - "0x", - ]); - }); + BigNumber.from('0'), + '0x', + ]) + }) - it("should be able to send the same message twice", async () => { - await OVM_L1CrossDomainMessenger.sendMessage(target, message, gasLimit); + it('should be able to send the same message twice', async () => { + await OVM_L1CrossDomainMessenger.sendMessage(target, message, gasLimit) await expect( OVM_L1CrossDomainMessenger.sendMessage(target, message, gasLimit) - ).to.not.be.reverted; - }); - }); + ).to.not.be.reverted + }) + }) - describe("replayMessage", () => { - const target = NON_ZERO_ADDRESS; - const message = NON_NULL_BYTES32; - const gasLimit = 100_000; + describe('replayMessage', () => { + const target = NON_ZERO_ADDRESS + const message = NON_NULL_BYTES32 + const gasLimit = 100_000 - it("should revert if the message does not exist", async () => { + it('should revert if the message does not exist', async () => { await expect( OVM_L1CrossDomainMessenger.replayMessage( target, @@ -148,11 +148,11 @@ describe("OVM_L1CrossDomainMessenger", () => { 0, gasLimit ) - ).to.be.revertedWith("Provided message has not already been sent."); - }); + ).to.be.revertedWith('Provided message has not already been sent.') + }) - it("should succeed if the message exists", async () => { - await OVM_L1CrossDomainMessenger.sendMessage(target, message, gasLimit); + it('should succeed if the message exists', async () => { + await OVM_L1CrossDomainMessenger.sendMessage(target, message, gasLimit) await expect( OVM_L1CrossDomainMessenger.replayMessage( @@ -162,41 +162,41 @@ describe("OVM_L1CrossDomainMessenger", () => { 0, gasLimit ) - ).to.not.be.reverted; - }); - }); - - describe("relayMessage", () => { - let target: string; - let message: string; - let sender: string; - let proof: any; - let calldata: string; + ).to.not.be.reverted + }) + }) + + describe('relayMessage', () => { + let target: string + let message: string + let sender: string + let proof: any + let calldata: string before(async () => { - target = Mock__TargetContract.address; - message = Mock__TargetContract.interface.encodeFunctionData("setTarget", [ + target = Mock__TargetContract.address + message = Mock__TargetContract.interface.encodeFunctionData('setTarget', [ NON_ZERO_ADDRESS, - ]); - sender = await signer.getAddress(); + ]) + sender = await signer.getAddress() - calldata = getXDomainCalldata(sender, target, message, 0); + calldata = getXDomainCalldata(sender, target, message, 0) - const precompile = "0x4200000000000000000000000000000000000000"; + const precompile = '0x4200000000000000000000000000000000000000' const storageKey = keccak256( keccak256( calldata + remove0x(Mock__OVM_L2CrossDomainMessenger.address) - ) + "00".repeat(32) - ); + ) + '00'.repeat(32) + ) const storageGenerator = await TrieTestGenerator.fromNodes({ nodes: [ { key: storageKey, - val: "0x" + "01".padStart(2, "0"), + val: '0x' + '01'.padStart(2, '0'), }, ], secure: true, - }); + }) const generator = await TrieTestGenerator.fromAccounts({ accounts: [ @@ -204,12 +204,12 @@ describe("OVM_L1CrossDomainMessenger", () => { address: precompile, nonce: 0, balance: 0, - codeHash: keccak256("0x1234"), + codeHash: keccak256('0x1234'), storageRoot: toHexString(storageGenerator._trie.root), }, ], secure: true, - }); + }) proof = { stateRoot: toHexString(generator._trie.root), @@ -220,30 +220,30 @@ describe("OVM_L1CrossDomainMessenger", () => { storageTrieWitness: ( await storageGenerator.makeInclusionProofTest(storageKey) ).proof, - }; - }); + } + }) beforeEach(async () => { Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with( true - ); + ) Mock__OVM_StateCommitmentChain.smocked.insideFraudProofWindow.will.return.with( false - ); - }); + ) + }) - it("should revert if still inside the fraud proof window", async () => { + it('should revert if still inside the fraud proof window', async () => { Mock__OVM_StateCommitmentChain.smocked.insideFraudProofWindow.will.return.with( true - ); + ) const proof1 = { stateRoot: NULL_BYTES32, stateRootBatchHeader: DUMMY_BATCH_HEADERS[0], stateRootProof: DUMMY_BATCH_PROOFS[0], - stateTrieWitness: "0x", - storageTrieWitness: "0x", - }; + stateTrieWitness: '0x', + storageTrieWitness: '0x', + } await expect( OVM_L1CrossDomainMessenger.relayMessage( @@ -253,21 +253,21 @@ describe("OVM_L1CrossDomainMessenger", () => { 0, proof1 ) - ).to.be.revertedWith("Provided message could not be verified."); - }); + ).to.be.revertedWith('Provided message could not be verified.') + }) - it("should revert if provided an invalid state root proof", async () => { + it('should revert if provided an invalid state root proof', async () => { Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with( false - ); + ) const proof1 = { stateRoot: NULL_BYTES32, stateRootBatchHeader: DUMMY_BATCH_HEADERS[0], stateRootProof: DUMMY_BATCH_PROOFS[0], - stateTrieWitness: "0x", - storageTrieWitness: "0x", - }; + stateTrieWitness: '0x', + storageTrieWitness: '0x', + } await expect( OVM_L1CrossDomainMessenger.relayMessage( @@ -277,29 +277,29 @@ describe("OVM_L1CrossDomainMessenger", () => { 0, proof1 ) - ).to.be.revertedWith("Provided message could not be verified."); - }); + ).to.be.revertedWith('Provided message could not be verified.') + }) - it("should revert if provided an invalid storage trie witness", async () => { + it('should revert if provided an invalid storage trie witness', async () => { await expect( OVM_L1CrossDomainMessenger.relayMessage(target, sender, message, 0, { ...proof, - storageTrieWitness: "0x", + storageTrieWitness: '0x', }) - ).to.be.reverted; - }); + ).to.be.reverted + }) - it("should revert if provided an invalid state trie witness", async () => { + it('should revert if provided an invalid state trie witness', async () => { await expect( OVM_L1CrossDomainMessenger.relayMessage(target, sender, message, 0, { ...proof, - stateTrieWitness: "0x", + stateTrieWitness: '0x', }) - ).to.be.reverted; - }); + ).to.be.reverted + }) - it("should send a successful call to the target contract", async () => { - const blockNumber = await getNextBlockNumber(ethers.provider); + it('should send a successful call to the target contract', async () => { + const blockNumber = await getNextBlockNumber(ethers.provider) await OVM_L1CrossDomainMessenger.relayMessage( target, @@ -307,11 +307,11 @@ describe("OVM_L1CrossDomainMessenger", () => { message, 0, proof - ); + ) expect( await OVM_L1CrossDomainMessenger.successfulMessages(keccak256(calldata)) - ).to.equal(true); + ).to.equal(true) expect( await OVM_L1CrossDomainMessenger.relayedMessages( @@ -320,21 +320,21 @@ describe("OVM_L1CrossDomainMessenger", () => { remove0x(await signer.getAddress()) + remove0x(BigNumber.from(blockNumber).toHexString()).padStart( 64, - "0" + '0' ) ) ) - ).to.equal(true); - }); + ).to.equal(true) + }) - it("should revert if trying to send the same message twice", async () => { + it('should revert if trying to send the same message twice', async () => { await OVM_L1CrossDomainMessenger.relayMessage( target, sender, message, 0, proof - ); + ) await expect( OVM_L1CrossDomainMessenger.relayMessage( @@ -344,15 +344,15 @@ describe("OVM_L1CrossDomainMessenger", () => { 0, proof ) - ).to.be.revertedWith("Provided message has already been received."); - }); + ).to.be.revertedWith('Provided message has already been received.') + }) - it("when the OVM_L2MessageRelayer address is set, should revert if called by a different account", async () => { + it('when the OVM_L2MessageRelayer address is set, should revert if called by a different account', async () => { // set to a random NON-ZERO address await AddressManager.setAddress( - "OVM_L2MessageRelayer", - "0x1234123412341234123412341234123412341234" - ); + 'OVM_L2MessageRelayer', + '0x1234123412341234123412341234123412341234' + ) await expect( OVM_L1CrossDomainMessenger.relayMessage( @@ -363,8 +363,8 @@ describe("OVM_L1CrossDomainMessenger", () => { proof ) ).to.be.revertedWith( - "Only OVM_L2MessageRelayer can relay L2-to-L1 messages." - ); - }); - }); -}); + 'Only OVM_L2MessageRelayer can relay L2-to-L1 messages.' + ) + }) + }) +}) diff --git a/test/contracts/OVM/chain/OVM_CanonicalTransactionChain.spec.ts b/test/contracts/OVM/chain/OVM_CanonicalTransactionChain.spec.ts index 7fa5e8cd4..38c717f90 100644 --- a/test/contracts/OVM/chain/OVM_CanonicalTransactionChain.spec.ts +++ b/test/contracts/OVM/chain/OVM_CanonicalTransactionChain.spec.ts @@ -1,12 +1,12 @@ -import { expect } from "../../../setup"; +import { expect } from '../../../setup' /* External Imports */ -import { ethers } from "hardhat"; -import { Signer, ContractFactory, Contract, BigNumber } from "ethers"; -import { TransactionResponse } from "@ethersproject/abstract-provider"; -import { smockit, MockContract } from "@eth-optimism/smock"; -import { remove0x } from "@eth-optimism/core-utils"; -import _ from "lodash"; +import { ethers } from 'hardhat' +import { Signer, ContractFactory, Contract, BigNumber } from 'ethers' +import { TransactionResponse } from '@ethersproject/abstract-provider' +import { smockit, MockContract } from '@eth-optimism/smock' +import { remove0x } from '@eth-optimism/core-utils' +import _ from 'lodash' /* Internal Imports */ import { @@ -22,21 +22,21 @@ import { getBlockTime, ZERO_ADDRESS, mineBlock, -} from "../../../helpers"; -import { defaultAbiCoder, keccak256 } from "ethers/lib/utils"; +} from '../../../helpers' +import { defaultAbiCoder, keccak256 } from 'ethers/lib/utils' -const ELEMENT_TEST_SIZES = [1, 2, 4, 8, 16]; -const DECOMPRESSION_ADDRESS = "0x4200000000000000000000000000000000000008"; -const MAX_GAS_LIMIT = 8_000_000; +const ELEMENT_TEST_SIZES = [1, 2, 4, 8, 16] +const DECOMPRESSION_ADDRESS = '0x4200000000000000000000000000000000000008' +const MAX_GAS_LIMIT = 8_000_000 const getQueueLeafHash = (index: number): string => { return keccak256( defaultAbiCoder.encode( - ["bool", "uint256", "uint256", "uint256", "bytes"], - [false, index, 0, 0, "0x"] + ['bool', 'uint256', 'uint256', 'uint256', 'bytes'], + [false, index, 0, 0, '0x'] ) - ); -}; + ) +} const getSequencerLeafHash = ( timestamp: number, @@ -44,12 +44,12 @@ const getSequencerLeafHash = ( data: string ): string => { return keccak256( - "0x01" + - remove0x(BigNumber.from(timestamp).toHexString()).padStart(64, "0") + - remove0x(BigNumber.from(blockNumber).toHexString()).padStart(64, "0") + + '0x01' + + remove0x(BigNumber.from(timestamp).toHexString()).padStart(64, '0') + + remove0x(BigNumber.from(blockNumber).toHexString()).padStart(64, '0') + remove0x(data) - ); -}; + ) +} const getTransactionHash = ( sender: string, @@ -57,8 +57,8 @@ const getTransactionHash = ( gasLimit: number, data: string ): string => { - return keccak256(encodeQueueTransaction(sender, target, gasLimit, data)); -}; + return keccak256(encodeQueueTransaction(sender, target, gasLimit, data)) +} const encodeQueueTransaction = ( sender: string, @@ -67,203 +67,200 @@ const encodeQueueTransaction = ( data: string ): string => { return defaultAbiCoder.encode( - ["address", "address", "uint256", "bytes"], + ['address', 'address', 'uint256', 'bytes'], [sender, target, gasLimit, data] - ); -}; + ) +} interface BatchContext { - numSequencedTransactions: number; - numSubsequentQueueTransactions: number; - timestamp: number; - blockNumber: number; + numSequencedTransactions: number + numSubsequentQueueTransactions: number + timestamp: number + blockNumber: number } interface AppendSequencerBatchParams { - shouldStartAtElement: number; // 5 bytes -- starts at batch - totalElementsToAppend: number; // 3 bytes -- total_elements_to_append - contexts: BatchContext[]; // total_elements[fixed_size[]] - transactions: string[]; // total_size_bytes[],total_size_bytes[] + shouldStartAtElement: number // 5 bytes -- starts at batch + totalElementsToAppend: number // 3 bytes -- total_elements_to_append + contexts: BatchContext[] // total_elements[fixed_size[]] + transactions: string[] // total_size_bytes[],total_size_bytes[] } const encodeAppendSequencerBatch = (b: AppendSequencerBatchParams): string => { const encodedShouldStartAtElement = remove0x( BigNumber.from(b.shouldStartAtElement).toHexString() - ).padStart(10, "0"); + ).padStart(10, '0') const encodedTotalElementsToAppend = remove0x( BigNumber.from(b.totalElementsToAppend).toHexString() - ).padStart(6, "0"); + ).padStart(6, '0') const encodedContextsHeader = remove0x( BigNumber.from(b.contexts.length).toHexString() - ).padStart(6, "0"); + ).padStart(6, '0') const encodedContexts = encodedContextsHeader + - b.contexts.reduce((acc, cur) => acc + encodeBatchContext(cur), ""); + b.contexts.reduce((acc, cur) => acc + encodeBatchContext(cur), '') const encodedTransactionData = b.transactions.reduce((acc, cur) => { if (cur.length % 2 !== 0) - throw new Error("Unexpected uneven hex string value!"); + throw new Error('Unexpected uneven hex string value!') const encodedTxDataHeader = remove0x( BigNumber.from(remove0x(cur).length / 2).toHexString() - ).padStart(6, "0"); + ).padStart(6, '0') - return acc + encodedTxDataHeader + remove0x(cur); - }, ""); + return acc + encodedTxDataHeader + remove0x(cur) + }, '') return ( encodedShouldStartAtElement + encodedTotalElementsToAppend + encodedContexts + encodedTransactionData - ); -}; + ) +} const appendSequencerBatch = async ( OVM_CanonicalTransactionChain: Contract, batch: AppendSequencerBatchParams ): Promise => { - const methodId = keccak256(Buffer.from("appendSequencerBatch()")).slice( - 2, - 10 - ); - const calldata = encodeAppendSequencerBatch(batch); + const methodId = keccak256(Buffer.from('appendSequencerBatch()')).slice(2, 10) + const calldata = encodeAppendSequencerBatch(batch) return OVM_CanonicalTransactionChain.signer.sendTransaction({ to: OVM_CanonicalTransactionChain.address, - data: "0x" + methodId + calldata, - }); -}; + data: '0x' + methodId + calldata, + }) +} const encodeBatchContext = (context: BatchContext): string => { return ( remove0x( BigNumber.from(context.numSequencedTransactions).toHexString() - ).padStart(6, "0") + + ).padStart(6, '0') + remove0x( BigNumber.from(context.numSubsequentQueueTransactions).toHexString() - ).padStart(6, "0") + + ).padStart(6, '0') + remove0x(BigNumber.from(context.timestamp).toHexString()).padStart( 10, - "0" + '0' ) + remove0x(BigNumber.from(context.blockNumber).toHexString()).padStart( 10, - "0" + '0' ) - ); -}; + ) +} -describe("OVM_CanonicalTransactionChain", () => { - let signer: Signer; - let sequencer: Signer; +describe('OVM_CanonicalTransactionChain', () => { + let signer: Signer + let sequencer: Signer before(async () => { - [signer, sequencer] = await ethers.getSigners(); - }); + ;[signer, sequencer] = await ethers.getSigners() + }) - let AddressManager: Contract; - let Mock__OVM_ExecutionManager: MockContract; - let Mock__OVM_StateCommitmentChain: MockContract; + let AddressManager: Contract + let Mock__OVM_ExecutionManager: MockContract + let Mock__OVM_StateCommitmentChain: MockContract before(async () => { - AddressManager = await makeAddressManager(); + AddressManager = await makeAddressManager() await AddressManager.setAddress( - "OVM_Sequencer", + 'OVM_Sequencer', await sequencer.getAddress() - ); + ) await AddressManager.setAddress( - "OVM_DecompressionPrecompileAddress", + 'OVM_DecompressionPrecompileAddress', DECOMPRESSION_ADDRESS - ); + ) Mock__OVM_ExecutionManager = await smockit( - await ethers.getContractFactory("OVM_ExecutionManager") - ); + await ethers.getContractFactory('OVM_ExecutionManager') + ) Mock__OVM_StateCommitmentChain = await smockit( - await ethers.getContractFactory("OVM_StateCommitmentChain") - ); + await ethers.getContractFactory('OVM_StateCommitmentChain') + ) await setProxyTarget( AddressManager, - "OVM_ExecutionManager", + 'OVM_ExecutionManager', Mock__OVM_ExecutionManager - ); + ) await setProxyTarget( AddressManager, - "OVM_StateCommitmentChain", + 'OVM_StateCommitmentChain', Mock__OVM_StateCommitmentChain - ); + ) Mock__OVM_ExecutionManager.smocked.getMaxTransactionGasLimit.will.return.with( MAX_GAS_LIMIT - ); - }); + ) + }) - let Factory__OVM_CanonicalTransactionChain: ContractFactory; - let Factory__OVM_ChainStorageContainer: ContractFactory; + let Factory__OVM_CanonicalTransactionChain: ContractFactory + let Factory__OVM_ChainStorageContainer: ContractFactory before(async () => { Factory__OVM_CanonicalTransactionChain = await ethers.getContractFactory( - "OVM_CanonicalTransactionChain" - ); + 'OVM_CanonicalTransactionChain' + ) Factory__OVM_ChainStorageContainer = await ethers.getContractFactory( - "OVM_ChainStorageContainer" - ); - }); + 'OVM_ChainStorageContainer' + ) + }) - let OVM_CanonicalTransactionChain: Contract; + let OVM_CanonicalTransactionChain: Contract beforeEach(async () => { OVM_CanonicalTransactionChain = await Factory__OVM_CanonicalTransactionChain.deploy( AddressManager.address, FORCE_INCLUSION_PERIOD_SECONDS, FORCE_INCLUSION_PERIOD_BLOCKS, MAX_GAS_LIMIT - ); + ) const batches = await Factory__OVM_ChainStorageContainer.deploy( AddressManager.address, - "OVM_CanonicalTransactionChain" - ); + 'OVM_CanonicalTransactionChain' + ) const queue = await Factory__OVM_ChainStorageContainer.deploy( AddressManager.address, - "OVM_CanonicalTransactionChain" - ); + 'OVM_CanonicalTransactionChain' + ) await AddressManager.setAddress( - "OVM_ChainStorageContainer:CTC:batches", + 'OVM_ChainStorageContainer:CTC:batches', batches.address - ); + ) await AddressManager.setAddress( - "OVM_ChainStorageContainer:CTC:queue", + 'OVM_ChainStorageContainer:CTC:queue', queue.address - ); + ) await AddressManager.setAddress( - "OVM_CanonicalTransactionChain", + 'OVM_CanonicalTransactionChain', OVM_CanonicalTransactionChain.address - ); - }); + ) + }) - describe("enqueue", () => { - const target = NON_ZERO_ADDRESS; - const gasLimit = 500_000; + describe('enqueue', () => { + const target = NON_ZERO_ADDRESS + const gasLimit = 500_000 - it("should revert when trying to input more data than the max data size", async () => { - const MAX_ROLLUP_TX_SIZE = await OVM_CanonicalTransactionChain.MAX_ROLLUP_TX_SIZE(); - const data = "0x" + "12".repeat(MAX_ROLLUP_TX_SIZE + 1); + it('should revert when trying to input more data than the max data size', async () => { + const MAX_ROLLUP_TX_SIZE = await OVM_CanonicalTransactionChain.MAX_ROLLUP_TX_SIZE() + const data = '0x' + '12'.repeat(MAX_ROLLUP_TX_SIZE + 1) await expect( - OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data, 0, "0x") + OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data, 0, '0x') ).to.be.revertedWith( - "Transaction data size exceeds maximum for rollup transaction." - ); - }); + 'Transaction data size exceeds maximum for rollup transaction.' + ) + }) - it("should revert when trying to enqueue a transaction with a higher gasLimit than the max", async () => { - const data = "0x1234567890"; + it('should revert when trying to enqueue a transaction with a higher gasLimit than the max', async () => { + const data = '0x1234567890' await expect( OVM_CanonicalTransactionChain.enqueue( @@ -271,17 +268,17 @@ describe("OVM_CanonicalTransactionChain", () => { MAX_GAS_LIMIT + 1, data, 0, - "0x" + '0x' ) ).to.be.revertedWith( - "Transaction gas limit exceeds maximum for rollup transaction." - ); - }); + 'Transaction gas limit exceeds maximum for rollup transaction.' + ) + }) - it("should revert if gas limit parameter is not at least MIN_ROLLUP_TX_GAS", async () => { - const MIN_ROLLUP_TX_GAS = await OVM_CanonicalTransactionChain.MIN_ROLLUP_TX_GAS(); - const customGasLimit = MIN_ROLLUP_TX_GAS / 2; - const data = "0x" + "12".repeat(1234); + it('should revert if gas limit parameter is not at least MIN_ROLLUP_TX_GAS', async () => { + const MIN_ROLLUP_TX_GAS = await OVM_CanonicalTransactionChain.MIN_ROLLUP_TX_GAS() + const customGasLimit = MIN_ROLLUP_TX_GAS / 2 + const data = '0x' + '12'.repeat(1234) await expect( OVM_CanonicalTransactionChain.enqueue( @@ -289,36 +286,36 @@ describe("OVM_CanonicalTransactionChain", () => { customGasLimit, data, 0, - "0x" + '0x' ) - ).to.be.revertedWith("Transaction gas limit too low to enqueue."); - }); + ).to.be.revertedWith('Transaction gas limit too low to enqueue.') + }) - it("should revert if transaction gas limit does not cover rollup burn", async () => { - const L2_GAS_DISCOUNT_DIVISOR = await OVM_CanonicalTransactionChain.L2_GAS_DISCOUNT_DIVISOR(); - const data = "0x" + "12".repeat(1234); + it('should revert if transaction gas limit does not cover rollup burn', async () => { + const L2_GAS_DISCOUNT_DIVISOR = await OVM_CanonicalTransactionChain.L2_GAS_DISCOUNT_DIVISOR() + const data = '0x' + '12'.repeat(1234) await expect( - OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data, 0, "0x", { + OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data, 0, '0x', { gasLimit: gasLimit / L2_GAS_DISCOUNT_DIVISOR + 30_000, // offset constant overhead }) - ).to.be.revertedWith("Insufficient gas for L2 rate limiting burn."); - }); + ).to.be.revertedWith('Insufficient gas for L2 rate limiting burn.') + }) - describe("with valid input parameters", () => { - it("should emit a TransactionEnqueued event", async () => { - const timestamp = (await getEthTime(ethers.provider)) + 100; - const data = "0x" + "12".repeat(1234); + describe('with valid input parameters', () => { + it('should emit a TransactionEnqueued event', async () => { + const timestamp = (await getEthTime(ethers.provider)) + 100 + const data = '0x' + '12'.repeat(1234) - await setEthTime(ethers.provider, timestamp); + await setEthTime(ethers.provider, timestamp) await expect( - OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data, 0, "0x") - ).to.emit(OVM_CanonicalTransactionChain, "TransactionEnqueued"); - }); + OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data, 0, '0x') + ).to.emit(OVM_CanonicalTransactionChain, 'TransactionEnqueued') + }) - describe("when enqueing multiple times", () => { - const data = "0x" + "12".repeat(1234); + describe('when enqueing multiple times', () => { + const data = '0x' + '12'.repeat(1234) for (const size of ELEMENT_TEST_SIZES) { it(`should be able to enqueue ${size} elements`, async () => { @@ -329,58 +326,58 @@ describe("OVM_CanonicalTransactionChain", () => { gasLimit, data, 0, - "0x" + '0x' ) - ).to.not.be.reverted; + ).to.not.be.reverted } - }); + }) } - }); - }); - }); + }) + }) + }) - describe("getQueueElement", () => { - it("should revert when accessing a non-existent element", async () => { + describe('getQueueElement', () => { + it('should revert when accessing a non-existent element', async () => { await expect( OVM_CanonicalTransactionChain.getQueueElement(0) - ).to.be.revertedWith("Index out of bounds."); - }); + ).to.be.revertedWith('Index out of bounds.') + }) - describe("when the requested element exists", () => { - const target = NON_ZERO_ADDRESS; - const gasLimit = 500_000; - const data = "0x" + "12".repeat(1234); + describe('when the requested element exists', () => { + const target = NON_ZERO_ADDRESS + const gasLimit = 500_000 + const data = '0x' + '12'.repeat(1234) - describe("when getting the first element", () => { + describe('when getting the first element', () => { for (const size of ELEMENT_TEST_SIZES) { it(`gets the element when ${size} + 1 elements exist`, async () => { - const timestamp = (await getEthTime(ethers.provider)) + 100; - const blockNumber = await getNextBlockNumber(ethers.provider); - await setEthTime(ethers.provider, timestamp); + const timestamp = (await getEthTime(ethers.provider)) + 100 + const blockNumber = await getNextBlockNumber(ethers.provider) + await setEthTime(ethers.provider, timestamp) const queueRoot = getTransactionHash( await signer.getAddress(), target, gasLimit, data - ); + ) await OVM_CanonicalTransactionChain.enqueue( target, gasLimit, data, 0, - "0x" - ); + '0x' + ) for (let i = 0; i < size; i++) { await OVM_CanonicalTransactionChain.enqueue( target, gasLimit, - "0x" + "12".repeat(i + 1), + '0x' + '12'.repeat(i + 1), 0, - "0x" - ); + '0x' + ) } expect( @@ -391,47 +388,47 @@ describe("OVM_CanonicalTransactionChain", () => { queueRoot, timestamp, blockNumber, - }); - }); + }) + }) } - }); + }) - describe("when getting the middle element", () => { + describe('when getting the middle element', () => { for (const size of ELEMENT_TEST_SIZES) { it(`gets the element when ${size} elements exist`, async () => { - let timestamp: number; - let blockNumber: number; - let queueRoot: string; + let timestamp: number + let blockNumber: number + let queueRoot: string - const middleIndex = Math.floor(size / 2); + const middleIndex = Math.floor(size / 2) for (let i = 0; i < size; i++) { if (i === middleIndex) { - timestamp = (await getEthTime(ethers.provider)) + 100; - blockNumber = await getNextBlockNumber(ethers.provider); - await setEthTime(ethers.provider, timestamp); + timestamp = (await getEthTime(ethers.provider)) + 100 + blockNumber = await getNextBlockNumber(ethers.provider) + await setEthTime(ethers.provider, timestamp) queueRoot = getTransactionHash( await signer.getAddress(), target, gasLimit, data - ); + ) await OVM_CanonicalTransactionChain.enqueue( target, gasLimit, data, 0, - "0x" - ); + '0x' + ) } else { await OVM_CanonicalTransactionChain.enqueue( target, gasLimit, - "0x" + "12".repeat(i + 1), + '0x' + '12'.repeat(i + 1), 0, - "0x" - ); + '0x' + ) } } @@ -443,46 +440,46 @@ describe("OVM_CanonicalTransactionChain", () => { queueRoot, timestamp, blockNumber, - }); - }); + }) + }) } - }); + }) - describe("when getting the last element", () => { + describe('when getting the last element', () => { for (const size of ELEMENT_TEST_SIZES) { it(`gets the element when ${size} elements exist`, async () => { - let timestamp: number; - let blockNumber: number; - let queueRoot: string; + let timestamp: number + let blockNumber: number + let queueRoot: string for (let i = 0; i < size; i++) { if (i === size - 1) { - timestamp = (await getEthTime(ethers.provider)) + 100; - blockNumber = await getNextBlockNumber(ethers.provider); - await setEthTime(ethers.provider, timestamp); + timestamp = (await getEthTime(ethers.provider)) + 100 + blockNumber = await getNextBlockNumber(ethers.provider) + await setEthTime(ethers.provider, timestamp) queueRoot = getTransactionHash( await signer.getAddress(), target, gasLimit, data - ); + ) await OVM_CanonicalTransactionChain.enqueue( target, gasLimit, data, 0, - "0x" - ); + '0x' + ) } else { await OVM_CanonicalTransactionChain.enqueue( target, gasLimit, - "0x" + "12".repeat(i + 1), + '0x' + '12'.repeat(i + 1), 0, - "0x" - ); + '0x' + ) } } @@ -494,38 +491,38 @@ describe("OVM_CanonicalTransactionChain", () => { queueRoot, timestamp, blockNumber, - }); - }); + }) + }) } - }); - }); - }); + }) + }) + }) - describe("appendQueueBatch disabled", () => { - it("should revert", async () => { + describe('appendQueueBatch disabled', () => { + it('should revert', async () => { await expect( - OVM_CanonicalTransactionChain.appendQueueBatch(0, 0, "0x") - ).to.be.revertedWith("appendQueueBatch is currently disabled."); - }); - }); + OVM_CanonicalTransactionChain.appendQueueBatch(0, 0, '0x') + ).to.be.revertedWith('appendQueueBatch is currently disabled.') + }) + }) - describe.skip("appendQueueBatch", () => { - it("should revert if trying to append zero transactions", async () => { + describe.skip('appendQueueBatch', () => { + it('should revert if trying to append zero transactions', async () => { await expect( - OVM_CanonicalTransactionChain.appendQueueBatch(0, 0, "0x") - ).to.be.revertedWith("Must append more than zero transactions."); - }); + OVM_CanonicalTransactionChain.appendQueueBatch(0, 0, '0x') + ).to.be.revertedWith('Must append more than zero transactions.') + }) - it("should revert if the queue is empty", async () => { + it('should revert if the queue is empty', async () => { await expect( - OVM_CanonicalTransactionChain.appendQueueBatch(1, 0, "0x") - ).to.be.revertedWith("Must append more than zero transactions."); - }); + OVM_CanonicalTransactionChain.appendQueueBatch(1, 0, '0x') + ).to.be.revertedWith('Must append more than zero transactions.') + }) - describe("when the queue is not empty", () => { - const target = NON_ZERO_ADDRESS; - const gasLimit = 500_000; - const data = "0x" + "12".repeat(1234); + describe('when the queue is not empty', () => { + const target = NON_ZERO_ADDRESS + const gasLimit = 500_000 + const data = '0x' + '12'.repeat(1234) for (const size of ELEMENT_TEST_SIZES) { describe(`when the queue has ${size} elements`, () => { @@ -536,94 +533,94 @@ describe("OVM_CanonicalTransactionChain", () => { gasLimit, data, 0, - "0x" - ); + '0x' + ) } - }); + }) - describe("when the sequencer inclusion period has not passed", () => { - it("should revert if not called by the sequencer", async () => { + describe('when the sequencer inclusion period has not passed', () => { + it('should revert if not called by the sequencer', async () => { await expect( OVM_CanonicalTransactionChain.connect(signer).appendQueueBatch( 1, 0, - "0x" + '0x' ) ).to.be.revertedWith( - "Queue transactions cannot be submitted during the sequencer inclusion period." - ); - }); + 'Queue transactions cannot be submitted during the sequencer inclusion period.' + ) + }) - it("should succeed if called by the sequencer", async () => { + it('should succeed if called by the sequencer', async () => { await expect( OVM_CanonicalTransactionChain.connect( sequencer - ).appendQueueBatch(1, 0, "0x") + ).appendQueueBatch(1, 0, '0x') ) - .to.emit(OVM_CanonicalTransactionChain, "QueueBatchAppended") - .withArgs(0, 1, 1); - }); - }); + .to.emit(OVM_CanonicalTransactionChain, 'QueueBatchAppended') + .withArgs(0, 1, 1) + }) + }) - describe("when the sequencer inclusion period has passed", () => { + describe('when the sequencer inclusion period has passed', () => { beforeEach(async () => { await increaseEthTime( ethers.provider, FORCE_INCLUSION_PERIOD_SECONDS * 2 - ); - }); + ) + }) - it("should be able to append a single element", async () => { + it('should be able to append a single element', async () => { await expect( - OVM_CanonicalTransactionChain.appendQueueBatch(1, 0, "0x") + OVM_CanonicalTransactionChain.appendQueueBatch(1, 0, '0x') ) - .to.emit(OVM_CanonicalTransactionChain, "QueueBatchAppended") - .withArgs(0, 1, 1); - }); + .to.emit(OVM_CanonicalTransactionChain, 'QueueBatchAppended') + .withArgs(0, 1, 1) + }) it(`should be able to append ${size} elements`, async () => { await expect( - OVM_CanonicalTransactionChain.appendQueueBatch(size, 0, "0x") + OVM_CanonicalTransactionChain.appendQueueBatch(size, 0, '0x') ) - .to.emit(OVM_CanonicalTransactionChain, "QueueBatchAppended") - .withArgs(0, size, size); - }); + .to.emit(OVM_CanonicalTransactionChain, 'QueueBatchAppended') + .withArgs(0, size, size) + }) it(`should be able to append ${size} elements even if attempting to append ${size} + 1 elements`, async () => { await expect( OVM_CanonicalTransactionChain.appendQueueBatch( size + 1, 0, - "0x" + '0x' ) ) - .to.emit(OVM_CanonicalTransactionChain, "QueueBatchAppended") - .withArgs(0, size, size); - }); - }); - }); + .to.emit(OVM_CanonicalTransactionChain, 'QueueBatchAppended') + .withArgs(0, size, size) + }) + }) + }) } - }); - }); + }) + }) - describe("verifyTransaction", () => { - it("should successfully verify against a valid queue transaction appended by the sequencer", async () => { - const entrypoint = NON_ZERO_ADDRESS; - const gasLimit = 500_000; - const data = "0x" + "12".repeat(1234); + describe('verifyTransaction', () => { + it('should successfully verify against a valid queue transaction appended by the sequencer', async () => { + const entrypoint = NON_ZERO_ADDRESS + const gasLimit = 500_000 + const data = '0x' + '12'.repeat(1234) - const timestamp = (await getEthTime(ethers.provider)) + 100; - await setEthTime(ethers.provider, timestamp); + const timestamp = (await getEthTime(ethers.provider)) + 100 + await setEthTime(ethers.provider, timestamp) await OVM_CanonicalTransactionChain.enqueue( entrypoint, gasLimit, data, 0, - "0x" - ); + '0x' + ) - const blockNumber = await ethers.provider.getBlockNumber(); + const blockNumber = await ethers.provider.getBlockNumber() await appendSequencerBatch( OVM_CanonicalTransactionChain.connect(sequencer), @@ -640,7 +637,7 @@ describe("OVM_CanonicalTransactionChain", () => { ], transactions: [], } - ); + ) expect( await OVM_CanonicalTransactionChain.verifyTransaction( @@ -658,45 +655,42 @@ describe("OVM_CanonicalTransactionChain", () => { queueIndex: 0, timestamp: 0, blockNumber: 0, - txData: "0x", + txData: '0x', }, { batchIndex: 0, batchRoot: getQueueLeafHash(0), batchSize: 1, prevTotalElements: 0, - extraData: "0x", + extraData: '0x', }, { index: 0, siblings: [], } ) - ).to.equal(true); - }); + ).to.equal(true) + }) - it.skip("should successfully verify against a valid queue transaction appended by force", async () => { - const entrypoint = NON_ZERO_ADDRESS; - const gasLimit = 500_000; - const data = "0x" + "12".repeat(1234); + it.skip('should successfully verify against a valid queue transaction appended by force', async () => { + const entrypoint = NON_ZERO_ADDRESS + const gasLimit = 500_000 + const data = '0x' + '12'.repeat(1234) - const timestamp = (await getEthTime(ethers.provider)) + 100; - await setEthTime(ethers.provider, timestamp); + const timestamp = (await getEthTime(ethers.provider)) + 100 + await setEthTime(ethers.provider, timestamp) await OVM_CanonicalTransactionChain.enqueue( entrypoint, gasLimit, data, 0, - "0x" - ); + '0x' + ) - const blockNumber = await ethers.provider.getBlockNumber(); - await increaseEthTime( - ethers.provider, - FORCE_INCLUSION_PERIOD_SECONDS * 2 - ); + const blockNumber = await ethers.provider.getBlockNumber() + await increaseEthTime(ethers.provider, FORCE_INCLUSION_PERIOD_SECONDS * 2) - await OVM_CanonicalTransactionChain.appendQueueBatch(1, 0, "0x"); + await OVM_CanonicalTransactionChain.appendQueueBatch(1, 0, '0x') expect( await OVM_CanonicalTransactionChain.verifyTransaction( @@ -714,29 +708,29 @@ describe("OVM_CanonicalTransactionChain", () => { queueIndex: 0, timestamp: 0, blockNumber: 0, - txData: "0x", + txData: '0x', }, { batchIndex: 0, batchRoot: getQueueLeafHash(0), batchSize: 1, prevTotalElements: 0, - extraData: "0x", + extraData: '0x', }, { index: 0, siblings: [], } ) - ).to.equal(true); - }); + ).to.equal(true) + }) - it("should successfully verify against a valid sequencer transaction", async () => { - const entrypoint = DECOMPRESSION_ADDRESS; - const gasLimit = MAX_GAS_LIMIT; - const data = "0x" + "12".repeat(1234); - const timestamp = (await getEthTime(ethers.provider)) - 10; - const blockNumber = (await ethers.provider.getBlockNumber()) - 1; + it('should successfully verify against a valid sequencer transaction', async () => { + const entrypoint = DECOMPRESSION_ADDRESS + const gasLimit = MAX_GAS_LIMIT + const data = '0x' + '12'.repeat(1234) + const timestamp = (await getEthTime(ethers.provider)) - 10 + const blockNumber = (await ethers.provider.getBlockNumber()) - 1 await appendSequencerBatch( OVM_CanonicalTransactionChain.connect(sequencer), @@ -753,7 +747,7 @@ describe("OVM_CanonicalTransactionChain", () => { ], transactions: [data], } - ); + ) expect( await OVM_CanonicalTransactionChain.verifyTransaction( @@ -778,27 +772,27 @@ describe("OVM_CanonicalTransactionChain", () => { batchRoot: getSequencerLeafHash(timestamp, blockNumber, data), batchSize: 1, prevTotalElements: 0, - extraData: "0x", + extraData: '0x', }, { index: 0, siblings: [], } ) - ).to.equal(true); - }); - }); + ).to.equal(true) + }) + }) - describe("appendSequencerBatch", () => { + describe('appendSequencerBatch', () => { beforeEach(() => { OVM_CanonicalTransactionChain = OVM_CanonicalTransactionChain.connect( sequencer - ); - }); + ) + }) - it("should allow for a lower bound per-tx gas usage of <400 gas [GAS BENCHMARK]", async () => { - const timestamp = (await getEthTime(ethers.provider)) - 100; - const blockNumber = await getNextBlockNumber(ethers.provider); + it('should allow for a lower bound per-tx gas usage of <400 gas [GAS BENCHMARK]', async () => { + const timestamp = (await getEthTime(ethers.provider)) - 100 + const blockNumber = await getNextBlockNumber(ethers.provider) // do two batch appends for no reason await appendSequencerBatch(OVM_CanonicalTransactionChain, { @@ -812,8 +806,8 @@ describe("OVM_CanonicalTransactionChain", () => { blockNumber, }, ], - transactions: ["0x1234"], - }); + transactions: ['0x1234'], + }) await appendSequencerBatch(OVM_CanonicalTransactionChain, { shouldStartAtElement: 1, totalElementsToAppend: 1, @@ -825,16 +819,16 @@ describe("OVM_CanonicalTransactionChain", () => { blockNumber, }, ], - transactions: ["0x1234"], - }); + transactions: ['0x1234'], + }) - console.log("\n~~~~ BEGINNGING TRASACTION IN QUESTION ~~~~"); - const transactions = []; - const numTxs = 200; + console.log('\n~~~~ BEGINNGING TRASACTION IN QUESTION ~~~~') + const transactions = [] + const numTxs = 200 for (let i = 0; i < numTxs; i++) { transactions.push( - "0x" + "1080111111111111111111111111111111111111111111".repeat(20) - ); + '0x' + '1080111111111111111111111111111111111111111111'.repeat(20) + ) } const res = await appendSequencerBatch(OVM_CanonicalTransactionChain, { shouldStartAtElement: 2, @@ -848,15 +842,15 @@ describe("OVM_CanonicalTransactionChain", () => { }, ], transactions, - }); - const receipt = await res.wait(); - console.log("Benchmark complete. Gas used:", receipt.gasUsed); - }).timeout(100000000); + }) + const receipt = await res.wait() + console.log('Benchmark complete. Gas used:', receipt.gasUsed) + }).timeout(100000000) - it("should revert if expected start does not match current total batches", async () => { + it('should revert if expected start does not match current total batches', async () => { await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ["0x1234"], + transactions: ['0x1234'], contexts: [ { numSequencedTransactions: 0, @@ -869,17 +863,17 @@ describe("OVM_CanonicalTransactionChain", () => { totalElementsToAppend: 1, }) ).to.be.revertedWith( - "Actual batch start index does not match expected start index." - ); - }); + 'Actual batch start index does not match expected start index.' + ) + }) - it("should revert if not all sequencer transactions are processed", async () => { - const timestamp = await getEthTime(ethers.provider); - const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1; + it('should revert if not all sequencer transactions are processed', async () => { + const timestamp = await getEthTime(ethers.provider) + const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1 await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ["0x1234", "0x1234"], + transactions: ['0x1234', '0x1234'], contexts: [ { numSequencedTransactions: 0, @@ -891,13 +885,13 @@ describe("OVM_CanonicalTransactionChain", () => { shouldStartAtElement: 0, totalElementsToAppend: 1, }) - ).to.be.revertedWith("Not all sequencer transactions were processed."); - }); + ).to.be.revertedWith('Not all sequencer transactions were processed.') + }) - it("should revert if not called by the sequencer", async () => { + it('should revert if not called by the sequencer', async () => { await expect( appendSequencerBatch(OVM_CanonicalTransactionChain.connect(signer), { - transactions: ["0x1234"], + transactions: ['0x1234'], contexts: [ { numSequencedTransactions: 0, @@ -909,24 +903,24 @@ describe("OVM_CanonicalTransactionChain", () => { shouldStartAtElement: 0, totalElementsToAppend: 1, }) - ).to.be.revertedWith("Function can only be called by the Sequencer."); - }); + ).to.be.revertedWith('Function can only be called by the Sequencer.') + }) - it("should revert if no contexts are provided", async () => { + it('should revert if no contexts are provided', async () => { await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ["0x1234"], + transactions: ['0x1234'], contexts: [], shouldStartAtElement: 0, totalElementsToAppend: 1, }) - ).to.be.revertedWith("Must provide at least one batch context."); - }); + ).to.be.revertedWith('Must provide at least one batch context.') + }) - it("should revert if total elements to append is zero", async () => { + it('should revert if total elements to append is zero', async () => { await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ["0x1234"], + transactions: ['0x1234'], contexts: [ { numSequencedTransactions: 0, @@ -938,22 +932,22 @@ describe("OVM_CanonicalTransactionChain", () => { shouldStartAtElement: 0, totalElementsToAppend: 0, }) - ).to.be.revertedWith("Must append at least one element."); - }); + ).to.be.revertedWith('Must append at least one element.') + }) - describe("Sad path cases", () => { - const target = NON_ZERO_ADDRESS; - const gasLimit = 500_000; - const data = "0x" + "12".repeat(1234); + describe('Sad path cases', () => { + const target = NON_ZERO_ADDRESS + const gasLimit = 500_000 + const data = '0x' + '12'.repeat(1234) - describe("when the sequencer attempts to add more queue transactions than exist", () => { - it("reverts when there are zero transactions in the queue", async () => { - const timestamp = await getEthTime(ethers.provider); - const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1; + describe('when the sequencer attempts to add more queue transactions than exist', () => { + it('reverts when there are zero transactions in the queue', async () => { + const timestamp = await getEthTime(ethers.provider) + const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1 await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ["0x1234"], + transactions: ['0x1234'], contexts: [ { numSequencedTransactions: 1, @@ -965,27 +959,27 @@ describe("OVM_CanonicalTransactionChain", () => { shouldStartAtElement: 0, totalElementsToAppend: 1, }) - ).to.be.revertedWith("Not enough queued transactions to append."); - }); + ).to.be.revertedWith('Not enough queued transactions to append.') + }) - it("reverts when there are insufficient (but nonzero) transactions in the queue", async () => { - const timestamp = await getEthTime(ethers.provider); - const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1; + it('reverts when there are insufficient (but nonzero) transactions in the queue', async () => { + const timestamp = await getEthTime(ethers.provider) + const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1 - const numEnqueues = 7; + const numEnqueues = 7 for (let i = 0; i < numEnqueues; i++) { await OVM_CanonicalTransactionChain.enqueue( target, gasLimit, data, 0, - "0x" - ); + '0x' + ) } await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ["0x1234"], + transactions: ['0x1234'], contexts: [ { numSequencedTransactions: 1, @@ -997,19 +991,19 @@ describe("OVM_CanonicalTransactionChain", () => { shouldStartAtElement: 0, totalElementsToAppend: numEnqueues + 1, }) - ).to.be.revertedWith("Not enough queued transactions to append."); - }); - }); + ).to.be.revertedWith('Not enough queued transactions to append.') + }) + }) - describe("when the sequencer attempts to add transactions which are not monotonically increasing", () => { - describe("when the sequencer transactions themselves have out-of-order times", () => { - it("should revert when adding two out-of-order-timestamp sequencer elements", async () => { - const timestamp = await getEthTime(ethers.provider); - const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1; + describe('when the sequencer attempts to add transactions which are not monotonically increasing', () => { + describe('when the sequencer transactions themselves have out-of-order times', () => { + it('should revert when adding two out-of-order-timestamp sequencer elements', async () => { + const timestamp = await getEthTime(ethers.provider) + const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1 await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ["0x1234", "0x5678"], + transactions: ['0x1234', '0x5678'], contexts: [ { numSequencedTransactions: 1, @@ -1028,17 +1022,17 @@ describe("OVM_CanonicalTransactionChain", () => { totalElementsToAppend: 2, }) ).to.be.revertedWith( - "Context timestamp values must monotonically increase." - ); - }); + 'Context timestamp values must monotonically increase.' + ) + }) - it("should revert when adding two out-of-order-blocknumber sequencer elements", async () => { - const timestamp = await getEthTime(ethers.provider); - const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1; + it('should revert when adding two out-of-order-blocknumber sequencer elements', async () => { + const timestamp = await getEthTime(ethers.provider) + const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1 await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ["0x1234", "0x5678"], + transactions: ['0x1234', '0x5678'], contexts: [ { numSequencedTransactions: 1, @@ -1057,12 +1051,12 @@ describe("OVM_CanonicalTransactionChain", () => { totalElementsToAppend: 2, }) ).to.be.revertedWith( - "Context blockNumber values must monotonically increase." - ); - }); - }); - describe("when the elements are out-of-order with regards to pending queue elements", async () => { - describe("adding a single sequencer transaction with a single pending queue element", () => { + 'Context blockNumber values must monotonically increase.' + ) + }) + }) + describe('when the elements are out-of-order with regards to pending queue elements', async () => { + describe('adding a single sequencer transaction with a single pending queue element', () => { beforeEach(async () => { // enqueue a single element so that it is pending, but do not yet apply it await OVM_CanonicalTransactionChain.enqueue( @@ -1070,17 +1064,17 @@ describe("OVM_CanonicalTransactionChain", () => { gasLimit, data, 0, - "0x" - ); - }); - it("should revert if the first context timestamp is > the head queue element timestamp", async () => { - const timestamp = (await getEthTime(ethers.provider)) + 100; + '0x' + ) + }) + it('should revert if the first context timestamp is > the head queue element timestamp', async () => { + const timestamp = (await getEthTime(ethers.provider)) + 100 const blockNumber = - (await getNextBlockNumber(ethers.provider)) - 1; + (await getNextBlockNumber(ethers.provider)) - 1 await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ["0x1234"], + transactions: ['0x1234'], contexts: [ { numSequencedTransactions: 1, @@ -1093,18 +1087,18 @@ describe("OVM_CanonicalTransactionChain", () => { totalElementsToAppend: 1, }) ).to.be.revertedWith( - "Sequencer transaction timestamp exceeds that of next queue element." - ); - }); + 'Sequencer transaction timestamp exceeds that of next queue element.' + ) + }) - it("should revert if the context block number is > the head queue element block number", async () => { - const timestamp = (await getEthTime(ethers.provider)) - 100; + it('should revert if the context block number is > the head queue element block number', async () => { + const timestamp = (await getEthTime(ethers.provider)) - 100 const blockNumber = - (await getNextBlockNumber(ethers.provider)) + 100; + (await getNextBlockNumber(ethers.provider)) + 100 await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ["0x1234"], + transactions: ['0x1234'], contexts: [ { numSequencedTransactions: 1, @@ -1117,14 +1111,14 @@ describe("OVM_CanonicalTransactionChain", () => { totalElementsToAppend: 1, }) ).to.be.revertedWith( - "Sequencer transaction blockNumber exceeds that of next queue element." - ); - }); - }); - describe("adding multiple sequencer transactions with multiple pending queue elements", () => { - const numQueuedTransactions = 10; - const queueElements = []; - const validContexts = []; + 'Sequencer transaction blockNumber exceeds that of next queue element.' + ) + }) + }) + describe('adding multiple sequencer transactions with multiple pending queue elements', () => { + const numQueuedTransactions = 10 + const queueElements = [] + const validContexts = [] beforeEach(async () => { for (let i = 0; i < numQueuedTransactions; i++) { await OVM_CanonicalTransactionChain.enqueue( @@ -1132,78 +1126,78 @@ describe("OVM_CanonicalTransactionChain", () => { gasLimit, data, 0, - "0x" - ); + '0x' + ) queueElements[ i - ] = await OVM_CanonicalTransactionChain.getQueueElement(i); + ] = await OVM_CanonicalTransactionChain.getQueueElement(i) // this is a valid context for this TX validContexts[i] = { numSequencedTransactions: 1, numSubsequentQueueTransactions: 1, timestamp: queueElements[i].timestamp, blockNumber: queueElements[i].blockNumber, - }; + } } - }); + }) - it("does not revert for valid context", async () => { + it('does not revert for valid context', async () => { await appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: new Array(numQueuedTransactions).fill("0x1234"), + transactions: new Array(numQueuedTransactions).fill('0x1234'), contexts: validContexts, shouldStartAtElement: 0, totalElementsToAppend: 2 * numQueuedTransactions, - }); - }); + }) + }) - it("reverts if wrong timestamp in middle", async () => { - const invalidTimestampContexts = [...validContexts]; + it('reverts if wrong timestamp in middle', async () => { + const invalidTimestampContexts = [...validContexts] // put a bigger timestamp early invalidTimestampContexts[6].timestamp = - invalidTimestampContexts[8].timestamp; + invalidTimestampContexts[8].timestamp await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: new Array(numQueuedTransactions).fill("0x1234"), + transactions: new Array(numQueuedTransactions).fill('0x1234'), contexts: invalidTimestampContexts, shouldStartAtElement: 0, totalElementsToAppend: 2 * numQueuedTransactions, }) ).to.be.revertedWith( - "Sequencer transaction timestamp exceeds that of next queue element." - ); - }); + 'Sequencer transaction timestamp exceeds that of next queue element.' + ) + }) - it("reverts if wrong block number in the middle", async () => { - const invalidBlockNumberContexts = [...validContexts]; + it('reverts if wrong block number in the middle', async () => { + const invalidBlockNumberContexts = [...validContexts] // put a bigger block number early invalidBlockNumberContexts[6].blockNumber = - invalidBlockNumberContexts[8].blockNumber; + invalidBlockNumberContexts[8].blockNumber await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: new Array(numQueuedTransactions).fill("0x1234"), + transactions: new Array(numQueuedTransactions).fill('0x1234'), contexts: invalidBlockNumberContexts, shouldStartAtElement: 0, totalElementsToAppend: 2 * numQueuedTransactions, }) ).to.be.revertedWith( - "Sequencer transaction blockNumber exceeds that of next queue element." - ); - }); - }); - }); - }); - - describe("when the sequencer attempts to add transactions with out-of-bounds times", async () => { - describe("when trying to add elements from the future", () => { - it("reverts on initial timestamp in the future", async () => { - const timestamp = (await getEthTime(ethers.provider)) + 100_000_000; - const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1; + 'Sequencer transaction blockNumber exceeds that of next queue element.' + ) + }) + }) + }) + }) + + describe('when the sequencer attempts to add transactions with out-of-bounds times', async () => { + describe('when trying to add elements from the future', () => { + it('reverts on initial timestamp in the future', async () => { + const timestamp = (await getEthTime(ethers.provider)) + 100_000_000 + const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1 await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ["0x1234"], + transactions: ['0x1234'], contexts: [ { numSequencedTransactions: 1, @@ -1215,16 +1209,16 @@ describe("OVM_CanonicalTransactionChain", () => { shouldStartAtElement: 0, totalElementsToAppend: 1, }) - ).to.be.revertedWith("Context timestamp is from the future."); - }); + ).to.be.revertedWith('Context timestamp is from the future.') + }) - it("reverts on non-initial timestamp in the future", async () => { - const timestamp = await getEthTime(ethers.provider); - const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1; + it('reverts on non-initial timestamp in the future', async () => { + const timestamp = await getEthTime(ethers.provider) + const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1 await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ["0x1234", "0x1234"], + transactions: ['0x1234', '0x1234'], contexts: [ { numSequencedTransactions: 1, @@ -1242,16 +1236,16 @@ describe("OVM_CanonicalTransactionChain", () => { shouldStartAtElement: 0, totalElementsToAppend: 2, }) - ).to.be.revertedWith("Context timestamp is from the future."); - }); + ).to.be.revertedWith('Context timestamp is from the future.') + }) - it("reverts on initial blocknumber in the future", async () => { - const timestamp = await getEthTime(ethers.provider); - const blockNumber = (await getNextBlockNumber(ethers.provider)) + 1; + it('reverts on initial blocknumber in the future', async () => { + const timestamp = await getEthTime(ethers.provider) + const blockNumber = (await getNextBlockNumber(ethers.provider)) + 1 await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ["0x1234"], + transactions: ['0x1234'], contexts: [ { numSequencedTransactions: 1, @@ -1263,23 +1257,23 @@ describe("OVM_CanonicalTransactionChain", () => { shouldStartAtElement: 0, totalElementsToAppend: 1, }) - ).to.be.revertedWith("Context block number is from the future."); - }); - }); - }); - - describe("when trying to add elements which are older than the force inclusion period", async () => { - it("reverts for a timestamp older than the f.i.p. ago", async () => { - const timestamp = await getEthTime(ethers.provider); + ).to.be.revertedWith('Context block number is from the future.') + }) + }) + }) + + describe('when trying to add elements which are older than the force inclusion period', async () => { + it('reverts for a timestamp older than the f.i.p. ago', async () => { + const timestamp = await getEthTime(ethers.provider) await increaseEthTime( ethers.provider, FORCE_INCLUSION_PERIOD_SECONDS + 1 - ); - const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1; + ) + const blockNumber = (await getNextBlockNumber(ethers.provider)) - 1 await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ["0x1234"], + transactions: ['0x1234'], contexts: [ { numSequencedTransactions: 1, @@ -1291,19 +1285,19 @@ describe("OVM_CanonicalTransactionChain", () => { shouldStartAtElement: 0, totalElementsToAppend: 1, }) - ).to.be.revertedWith("Context timestamp too far in the past."); - }); + ).to.be.revertedWith('Context timestamp too far in the past.') + }) - it("reverts for a blockNumber older than the f.i.p. ago", async () => { - const timestamp = await getEthTime(ethers.provider); + it('reverts for a blockNumber older than the f.i.p. ago', async () => { + const timestamp = await getEthTime(ethers.provider) for (let i = 0; i < FORCE_INCLUSION_PERIOD_BLOCKS + 1; i++) { - await mineBlock(ethers.provider); + await mineBlock(ethers.provider) } await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ["0x1234"], + transactions: ['0x1234'], contexts: [ { numSequencedTransactions: 1, @@ -1315,19 +1309,19 @@ describe("OVM_CanonicalTransactionChain", () => { shouldStartAtElement: 0, totalElementsToAppend: 1, }) - ).to.be.revertedWith("Context block number too far in the past."); - }); - }); - - describe("when trying to add elements which are older than already existing CTC elements", () => { - let timestamp; - let blockNumber; - describe("when the most recent CTC element is a sequencer transaction", () => { + ).to.be.revertedWith('Context block number too far in the past.') + }) + }) + + describe('when trying to add elements which are older than already existing CTC elements', () => { + let timestamp + let blockNumber + describe('when the most recent CTC element is a sequencer transaction', () => { beforeEach(async () => { - timestamp = await getEthTime(ethers.provider); - blockNumber = (await getNextBlockNumber(ethers.provider)) - 1; + timestamp = await getEthTime(ethers.provider) + blockNumber = (await getNextBlockNumber(ethers.provider)) - 1 await appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ["0x1234"], + transactions: ['0x1234'], contexts: [ { numSequencedTransactions: 1, @@ -1338,13 +1332,13 @@ describe("OVM_CanonicalTransactionChain", () => { ], shouldStartAtElement: 0, totalElementsToAppend: 1, - }); - }); + }) + }) - it("reverts if timestamp is older than previous one", async () => { + it('reverts if timestamp is older than previous one', async () => { await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ["0x1234"], + transactions: ['0x1234'], contexts: [ { numSequencedTransactions: 1, @@ -1357,14 +1351,14 @@ describe("OVM_CanonicalTransactionChain", () => { totalElementsToAppend: 1, }) ).to.be.revertedWith( - "Context timestamp is lower than last submitted." - ); - }); + 'Context timestamp is lower than last submitted.' + ) + }) - it("reverts if block number is older than previous one", async () => { + it('reverts if block number is older than previous one', async () => { await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ["0x1234"], + transactions: ['0x1234'], contexts: [ { numSequencedTransactions: 1, @@ -1377,25 +1371,25 @@ describe("OVM_CanonicalTransactionChain", () => { totalElementsToAppend: 1, }) ).to.be.revertedWith( - "Context block number is lower than last submitted." - ); - }); - }); + 'Context block number is lower than last submitted.' + ) + }) + }) - describe("when the previous transaction is a queue transaction", () => { + describe('when the previous transaction is a queue transaction', () => { beforeEach(async () => { // enqueue - timestamp = await getEthTime(ethers.provider); - blockNumber = await getNextBlockNumber(ethers.provider); + timestamp = await getEthTime(ethers.provider) + blockNumber = await getNextBlockNumber(ethers.provider) await OVM_CanonicalTransactionChain.enqueue( target, gasLimit, data, 0, - "0x" - ); + '0x' + ) await appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ["0x1234"], + transactions: ['0x1234'], contexts: [ { numSequencedTransactions: 1, @@ -1406,13 +1400,13 @@ describe("OVM_CanonicalTransactionChain", () => { ], shouldStartAtElement: 0, totalElementsToAppend: 2, - }); - }); + }) + }) - it("reverts if timestamp is older than previous one", async () => { + it('reverts if timestamp is older than previous one', async () => { await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ["0x1234"], + transactions: ['0x1234'], contexts: [ { numSequencedTransactions: 1, @@ -1425,14 +1419,14 @@ describe("OVM_CanonicalTransactionChain", () => { totalElementsToAppend: 1, }) ).to.be.revertedWith( - "Context timestamp is lower than last submitted." - ); - }); + 'Context timestamp is lower than last submitted.' + ) + }) - it("reverts if block number is older than previous one", async () => { + it('reverts if block number is older than previous one', async () => { await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ["0x1234"], + transactions: ['0x1234'], contexts: [ { numSequencedTransactions: 1, @@ -1445,34 +1439,34 @@ describe("OVM_CanonicalTransactionChain", () => { totalElementsToAppend: 1, }) ).to.be.revertedWith( - "Context block number is lower than last submitted." - ); - }); - }); - }); + 'Context block number is lower than last submitted.' + ) + }) + }) + }) - it("should revert if a queue element has expired and needs to be included", async () => { + it('should revert if a queue element has expired and needs to be included', async () => { // enqueue a tx await OVM_CanonicalTransactionChain.enqueue( target, gasLimit, data, 0, - "0x" - ); + '0x' + ) // increase time past force inclusion period await increaseEthTime( ethers.provider, FORCE_INCLUSION_PERIOD_SECONDS * 2 - ); + ) - const blockNumber = (await ethers.provider.getBlockNumber()) - 1; + const blockNumber = (await ethers.provider.getBlockNumber()) - 1 - const validTimestamp = (await getBlockTime(ethers.provider)) + 100; + const validTimestamp = (await getBlockTime(ethers.provider)) + 100 await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { - transactions: ["0x1234"], + transactions: ['0x1234'], contexts: [ { numSequencedTransactions: 1, @@ -1485,25 +1479,25 @@ describe("OVM_CanonicalTransactionChain", () => { totalElementsToAppend: 1, }) ).to.be.revertedWith( - "Previously enqueued batches have expired and must be appended before a new sequencer batch." - ); - }); - }); + 'Previously enqueued batches have expired and must be appended before a new sequencer batch.' + ) + }) + }) for (const size of ELEMENT_TEST_SIZES) { - const target = NON_ZERO_ADDRESS; - const gasLimit = 500_000; - const data = "0x" + "12".repeat(1234); + const target = NON_ZERO_ADDRESS + const gasLimit = 500_000 + const data = '0x' + '12'.repeat(1234) describe(`Happy path: when appending ${size} sequencer transactions`, () => { - describe("when not inserting queue elements in between", () => { - describe("when using a single batch context", () => { - let contexts: any[]; - let transactions: any[]; + describe('when not inserting queue elements in between', () => { + describe('when using a single batch context', () => { + let contexts: any[] + let transactions: any[] beforeEach(async () => { - const timestamp = (await getEthTime(ethers.provider)) - 100; + const timestamp = (await getEthTime(ethers.provider)) - 100 const blockNumber = - (await getNextBlockNumber(ethers.provider)) - 10; + (await getNextBlockNumber(ethers.provider)) - 10 contexts = [ { @@ -1512,14 +1506,14 @@ describe("OVM_CanonicalTransactionChain", () => { timestamp, blockNumber, }, - ]; + ] transactions = [...Array(size)].map((el, idx) => { - return "0x" + "12" + "34".repeat(idx); - }); - }); + return '0x' + '12' + '34'.repeat(idx) + }) + }) - it("should append the given number of transactions", async () => { + it('should append the given number of transactions', async () => { await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { transactions, @@ -1530,14 +1524,14 @@ describe("OVM_CanonicalTransactionChain", () => { ) .to.emit( OVM_CanonicalTransactionChain, - "SequencerBatchAppended" + 'SequencerBatchAppended' ) - .withArgs(0, 0, size); - }); - }); - }); + .withArgs(0, 0, size) + }) + }) + }) - describe("when inserting queue elements in between", () => { + describe('when inserting queue elements in between', () => { beforeEach(async () => { for (let i = 0; i < size; i++) { await OVM_CanonicalTransactionChain.enqueue( @@ -1545,18 +1539,18 @@ describe("OVM_CanonicalTransactionChain", () => { gasLimit, data, 0, - "0x" - ); + '0x' + ) } - }); + }) - describe("between every other sequencer transaction", () => { - let contexts: any[]; - let transactions: any[]; + describe('between every other sequencer transaction', () => { + let contexts: any[] + let transactions: any[] beforeEach(async () => { - const timestamp = (await getEthTime(ethers.provider)) - 100; + const timestamp = (await getEthTime(ethers.provider)) - 100 const blockNumber = - (await getNextBlockNumber(ethers.provider)) - 50; + (await getNextBlockNumber(ethers.provider)) - 50 contexts = [...Array(size)].map(() => { return { @@ -1564,15 +1558,15 @@ describe("OVM_CanonicalTransactionChain", () => { numSubsequentQueueTransactions: 1, timestamp, blockNumber: Math.max(blockNumber, 0), - }; - }); + } + }) transactions = [...Array(size)].map((el, idx) => { - return "0x" + "12" + "34".repeat(idx); - }); - }); + return '0x' + '12' + '34'.repeat(idx) + }) + }) - it("should append the batch", async () => { + it('should append the batch', async () => { await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { transactions, @@ -1583,20 +1577,20 @@ describe("OVM_CanonicalTransactionChain", () => { ) .to.emit( OVM_CanonicalTransactionChain, - "SequencerBatchAppended" + 'SequencerBatchAppended' ) - .withArgs(0, size, size * 2); - }); - }); + .withArgs(0, size, size * 2) + }) + }) - const spacing = Math.max(Math.floor(size / 4), 1); + const spacing = Math.max(Math.floor(size / 4), 1) describe(`between every ${spacing} sequencer transaction`, () => { - let contexts: any[]; - let transactions: any[]; + let contexts: any[] + let transactions: any[] beforeEach(async () => { - const timestamp = (await getEthTime(ethers.provider)) - 100; + const timestamp = (await getEthTime(ethers.provider)) - 100 const blockNumber = - (await getNextBlockNumber(ethers.provider)) - 50; + (await getNextBlockNumber(ethers.provider)) - 50 contexts = [...Array(spacing)].map(() => { return { @@ -1604,15 +1598,15 @@ describe("OVM_CanonicalTransactionChain", () => { numSubsequentQueueTransactions: 1, timestamp, blockNumber: Math.max(blockNumber, 0), - }; - }); + } + }) transactions = [...Array(size)].map((el, idx) => { - return "0x" + "12" + "34".repeat(idx); - }); - }); + return '0x' + '12' + '34'.repeat(idx) + }) + }) - it("should append the batch", async () => { + it('should append the batch', async () => { await expect( appendSequencerBatch(OVM_CanonicalTransactionChain, { transactions, @@ -1623,28 +1617,26 @@ describe("OVM_CanonicalTransactionChain", () => { ) .to.emit( OVM_CanonicalTransactionChain, - "SequencerBatchAppended" + 'SequencerBatchAppended' ) - .withArgs(0, spacing, size + spacing); - }); - }); - }); - }); + .withArgs(0, spacing, size + spacing) + }) + }) + }) + }) } - }); + }) - describe("getTotalElements", () => { - it("should return zero when no elements exist", async () => { - expect(await OVM_CanonicalTransactionChain.getTotalElements()).to.equal( - 0 - ); - }); + describe('getTotalElements', () => { + it('should return zero when no elements exist', async () => { + expect(await OVM_CanonicalTransactionChain.getTotalElements()).to.equal(0) + }) for (const size of ELEMENT_TEST_SIZES) { describe(`when the sequencer inserts a batch of ${size} elements`, () => { beforeEach(async () => { - const timestamp = (await getEthTime(ethers.provider)) - 100; - const blockNumber = (await getNextBlockNumber(ethers.provider)) - 10; + const timestamp = (await getEthTime(ethers.provider)) - 100 + const blockNumber = (await getNextBlockNumber(ethers.provider)) - 10 const contexts = [ { @@ -1653,11 +1645,11 @@ describe("OVM_CanonicalTransactionChain", () => { timestamp, blockNumber: Math.max(blockNumber, 0), }, - ]; + ] const transactions = [...Array(size)].map((el, idx) => { - return "0x" + "12" + "34".repeat(idx); - }); + return '0x' + '12' + '34'.repeat(idx) + }) const res = await appendSequencerBatch( OVM_CanonicalTransactionChain.connect(sequencer), @@ -1667,16 +1659,16 @@ describe("OVM_CanonicalTransactionChain", () => { shouldStartAtElement: 0, totalElementsToAppend: size, } - ); - await res.wait(); - }); + ) + await res.wait() + }) it(`should return ${size}`, async () => { expect( await OVM_CanonicalTransactionChain.getTotalElements() - ).to.equal(size); - }); - }); + ).to.equal(size) + }) + }) } - }); -}); + }) +}) diff --git a/test/contracts/OVM/chain/OVM_StateCommitmentChain.spec.ts b/test/contracts/OVM/chain/OVM_StateCommitmentChain.spec.ts index 795ad6ba0..8cb6b3dfc 100644 --- a/test/contracts/OVM/chain/OVM_StateCommitmentChain.spec.ts +++ b/test/contracts/OVM/chain/OVM_StateCommitmentChain.spec.ts @@ -1,9 +1,9 @@ -import { expect } from "../../../setup"; +import { expect } from '../../../setup' /* External Imports */ -import { ethers } from "hardhat"; -import { Signer, ContractFactory, Contract } from "ethers"; -import { smockit, MockContract } from "@eth-optimism/smock"; +import { ethers } from 'hardhat' +import { Signer, ContractFactory, Contract } from 'ethers' +import { smockit, MockContract } from '@eth-optimism/smock' /* Internal Imports */ import { @@ -14,239 +14,239 @@ import { getEthTime, NULL_BYTES32, increaseEthTime, -} from "../../../helpers"; +} from '../../../helpers' -describe("OVM_StateCommitmentChain", () => { - let sequencer: Signer; - let user: Signer; +describe('OVM_StateCommitmentChain', () => { + let sequencer: Signer + let user: Signer before(async () => { - [sequencer, user] = await ethers.getSigners(); - }); + ;[sequencer, user] = await ethers.getSigners() + }) - let AddressManager: Contract; + let AddressManager: Contract before(async () => { - AddressManager = await makeAddressManager(); - }); + AddressManager = await makeAddressManager() + }) - let Mock__OVM_CanonicalTransactionChain: MockContract; - let Mock__OVM_BondManager: MockContract; + let Mock__OVM_CanonicalTransactionChain: MockContract + let Mock__OVM_BondManager: MockContract before(async () => { Mock__OVM_CanonicalTransactionChain = await smockit( - await ethers.getContractFactory("OVM_CanonicalTransactionChain") - ); + await ethers.getContractFactory('OVM_CanonicalTransactionChain') + ) await setProxyTarget( AddressManager, - "OVM_CanonicalTransactionChain", + 'OVM_CanonicalTransactionChain', Mock__OVM_CanonicalTransactionChain - ); + ) Mock__OVM_BondManager = await smockit( - await ethers.getContractFactory("OVM_BondManager") - ); + await ethers.getContractFactory('OVM_BondManager') + ) await setProxyTarget( AddressManager, - "OVM_BondManager", + 'OVM_BondManager', Mock__OVM_BondManager - ); + ) - Mock__OVM_BondManager.smocked.isCollateralized.will.return.with(true); + Mock__OVM_BondManager.smocked.isCollateralized.will.return.with(true) await AddressManager.setAddress( - "OVM_Sequencer", + 'OVM_Sequencer', await sequencer.getAddress() - ); - }); + ) + }) - let Factory__OVM_StateCommitmentChain: ContractFactory; - let Factory__OVM_ChainStorageContainer: ContractFactory; + let Factory__OVM_StateCommitmentChain: ContractFactory + let Factory__OVM_ChainStorageContainer: ContractFactory before(async () => { Factory__OVM_StateCommitmentChain = await ethers.getContractFactory( - "OVM_StateCommitmentChain" - ); + 'OVM_StateCommitmentChain' + ) Factory__OVM_ChainStorageContainer = await ethers.getContractFactory( - "OVM_ChainStorageContainer" - ); - }); + 'OVM_ChainStorageContainer' + ) + }) - let OVM_StateCommitmentChain: Contract; + let OVM_StateCommitmentChain: Contract beforeEach(async () => { OVM_StateCommitmentChain = await Factory__OVM_StateCommitmentChain.deploy( AddressManager.address, 60 * 60 * 24 * 7, // 1 week fraud proof window 60 * 30 // 30 minute sequencer publish window - ); + ) const batches = await Factory__OVM_ChainStorageContainer.deploy( AddressManager.address, - "OVM_StateCommitmentChain" - ); + 'OVM_StateCommitmentChain' + ) await AddressManager.setAddress( - "OVM_ChainStorageContainer:SCC:batches", + 'OVM_ChainStorageContainer:SCC:batches', batches.address - ); + ) await AddressManager.setAddress( - "OVM_StateCommitmentChain", + 'OVM_StateCommitmentChain', OVM_StateCommitmentChain.address - ); - }); + ) + }) - describe("appendStateBatch", () => { - describe("when the provided batch is empty", () => { - const batch = []; + describe('appendStateBatch', () => { + describe('when the provided batch is empty', () => { + const batch = [] - it("should revert", async () => { + it('should revert', async () => { await expect( - OVM_StateCommitmentChain.appendStateBatch(batch, 0, 0, "0x") - ).to.be.revertedWith("Cannot submit an empty state batch."); - }); - }); + OVM_StateCommitmentChain.appendStateBatch(batch, 0, 0, '0x') + ).to.be.revertedWith('Cannot submit an empty state batch.') + }) + }) - describe("when the provided batch is not empty", () => { - const batch = [NON_NULL_BYTES32]; + describe('when the provided batch is not empty', () => { + const batch = [NON_NULL_BYTES32] - describe("when start index does not match total elements", () => { - it("should revert", async () => { + describe('when start index does not match total elements', () => { + it('should revert', async () => { await expect( - OVM_StateCommitmentChain.appendStateBatch(batch, 1, 0, "0x") + OVM_StateCommitmentChain.appendStateBatch(batch, 1, 0, '0x') ).to.be.revertedWith( - "Actual batch start index does not match expected start index." - ); - }); - }); + 'Actual batch start index does not match expected start index.' + ) + }) + }) - describe("when submitting more elements than present in the OVM_CanonicalTransactionChain", () => { + describe('when submitting more elements than present in the OVM_CanonicalTransactionChain', () => { before(() => { Mock__OVM_CanonicalTransactionChain.smocked.getTotalElements.will.return.with( batch.length - 1 - ); - }); + ) + }) - it("should revert", async () => { + it('should revert', async () => { await expect( - OVM_StateCommitmentChain.appendStateBatch(batch, 0, 0, "0x") + OVM_StateCommitmentChain.appendStateBatch(batch, 0, 0, '0x') ).to.be.revertedWith( - "Number of state roots cannot exceed the number of canonical transactions." - ); - }); - }); + 'Number of state roots cannot exceed the number of canonical transactions.' + ) + }) + }) - describe("when not submitting more elements than present in the OVM_CanonicalTransactionChain", () => { + describe('when not submitting more elements than present in the OVM_CanonicalTransactionChain', () => { before(() => { Mock__OVM_CanonicalTransactionChain.smocked.getTotalElements.will.return.with( batch.length - ); - }); + ) + }) - it("should append the state batch", async () => { + it('should append the state batch', async () => { await expect( - OVM_StateCommitmentChain.appendStateBatch(batch, 0, 0, "0x") - ).to.not.be.reverted; - }); - }); + OVM_StateCommitmentChain.appendStateBatch(batch, 0, 0, '0x') + ).to.not.be.reverted + }) + }) - describe("when a sequencer submits ", () => { + describe('when a sequencer submits ', () => { beforeEach(async () => { Mock__OVM_CanonicalTransactionChain.smocked.getTotalElements.will.return.with( batch.length * 2 - ); + ) await OVM_StateCommitmentChain.connect(sequencer).appendStateBatch( batch, 0, 0, - "0x" - ); - }); + '0x' + ) + }) - describe("when inside sequencer publish window", () => { - it("should revert", async () => { + describe('when inside sequencer publish window', () => { + it('should revert', async () => { await expect( OVM_StateCommitmentChain.connect(user).appendStateBatch( batch, 1, 0, - "0x" + '0x' ) ).to.be.revertedWith( - "Cannot publish state roots within the sequencer publication window." - ); - }); - }); + 'Cannot publish state roots within the sequencer publication window.' + ) + }) + }) - describe("when outside sequencer publish window", () => { + describe('when outside sequencer publish window', () => { beforeEach(async () => { - const SEQUENCER_PUBLISH_WINDOW = await OVM_StateCommitmentChain.SEQUENCER_PUBLISH_WINDOW(); + const SEQUENCER_PUBLISH_WINDOW = await OVM_StateCommitmentChain.SEQUENCER_PUBLISH_WINDOW() await increaseEthTime( ethers.provider, SEQUENCER_PUBLISH_WINDOW.toNumber() + 1 - ); - }); + ) + }) - it("should succeed", async () => { + it('should succeed', async () => { await expect( OVM_StateCommitmentChain.connect(user).appendStateBatch( batch, 1, 0, - "0x" + '0x' ) - ).to.not.be.reverted; - }); - }); - }); - }); - }); - - describe("deleteStateBatch", () => { - const batch = [NON_NULL_BYTES32]; + ).to.not.be.reverted + }) + }) + }) + }) + }) + + describe('deleteStateBatch', () => { + const batch = [NON_NULL_BYTES32] const batchHeader = { batchIndex: 0, batchRoot: NON_NULL_BYTES32, batchSize: 1, prevTotalElements: 0, extraData: NULL_BYTES32, - }; + } beforeEach(async () => { Mock__OVM_CanonicalTransactionChain.smocked.getTotalElements.will.return.with( batch.length - ); - await OVM_StateCommitmentChain.appendStateBatch(batch, 0, 0, "0x"); + ) + await OVM_StateCommitmentChain.appendStateBatch(batch, 0, 0, '0x') batchHeader.extraData = ethers.utils.defaultAbiCoder.encode( - ["uint256", "address"], + ['uint256', 'address'], [await getEthTime(ethers.provider), await sequencer.getAddress()] - ); - }); + ) + }) - describe("when the sender is not the OVM_FraudVerifier", () => { + describe('when the sender is not the OVM_FraudVerifier', () => { before(async () => { - await AddressManager.setAddress("OVM_FraudVerifier", ZERO_ADDRESS); - }); + await AddressManager.setAddress('OVM_FraudVerifier', ZERO_ADDRESS) + }) - it("should revert", async () => { + it('should revert', async () => { await expect( - OVM_StateCommitmentChain.deleteStateBatch(batchHeader, 0, "0x") + OVM_StateCommitmentChain.deleteStateBatch(batchHeader, 0, '0x') ).to.be.revertedWith( - "State batches can only be deleted by the OVM_FraudVerifier." - ); - }); - }); + 'State batches can only be deleted by the OVM_FraudVerifier.' + ) + }) + }) - describe("when the sender is the OVM_FraudVerifier", () => { + describe('when the sender is the OVM_FraudVerifier', () => { before(async () => { await AddressManager.setAddress( - "OVM_FraudVerifier", + 'OVM_FraudVerifier', await sequencer.getAddress() - ); - }); + ) + }) - describe("when the provided batch index is greater than the total submitted", () => { - it("should revert", async () => { + describe('when the provided batch index is greater than the total submitted', () => { + it('should revert', async () => { await expect( OVM_StateCommitmentChain.deleteStateBatch( { @@ -254,140 +254,140 @@ describe("OVM_StateCommitmentChain", () => { batchIndex: 1, }, 0, - "0x" + '0x' ) - ).to.be.revertedWith("Index out of bounds."); - }); - }); + ).to.be.revertedWith('Index out of bounds.') + }) + }) - describe("when the provided batch index is not greater than the total submitted", () => { - describe("when the provided batch header is invalid", () => { - it("should revert", async () => { + describe('when the provided batch index is not greater than the total submitted', () => { + describe('when the provided batch header is invalid', () => { + it('should revert', async () => { await expect( OVM_StateCommitmentChain.deleteStateBatch( { ...batchHeader, - extraData: "0x" + "22".repeat(32), + extraData: '0x' + '22'.repeat(32), }, 0, - "0x" + '0x' ) - ).to.be.revertedWith("Invalid batch header."); - }); - }); + ).to.be.revertedWith('Invalid batch header.') + }) + }) - describe("when the provided batch header is valid", () => { - it("should remove the batch and all following batches", async () => { + describe('when the provided batch header is valid', () => { + it('should remove the batch and all following batches', async () => { await expect( - OVM_StateCommitmentChain.deleteStateBatch(batchHeader, 0, "0x") - ).to.not.be.reverted; - }); - }); - }); - }); - }); - - describe("getTotalElements", () => { - describe("when no batch elements have been inserted", () => { - it("should return zero", async () => { - expect(await OVM_StateCommitmentChain.getTotalElements()).to.equal(0); - }); - }); - - describe("when one batch element has been inserted", () => { + OVM_StateCommitmentChain.deleteStateBatch(batchHeader, 0, '0x') + ).to.not.be.reverted + }) + }) + }) + }) + }) + + describe('getTotalElements', () => { + describe('when no batch elements have been inserted', () => { + it('should return zero', async () => { + expect(await OVM_StateCommitmentChain.getTotalElements()).to.equal(0) + }) + }) + + describe('when one batch element has been inserted', () => { beforeEach(async () => { - const batch = [NON_NULL_BYTES32]; + const batch = [NON_NULL_BYTES32] Mock__OVM_CanonicalTransactionChain.smocked.getTotalElements.will.return.with( batch.length - ); - await OVM_StateCommitmentChain.appendStateBatch(batch, 0, 0, "0x"); - }); + ) + await OVM_StateCommitmentChain.appendStateBatch(batch, 0, 0, '0x') + }) - it("should return the number of inserted batch elements", async () => { - expect(await OVM_StateCommitmentChain.getTotalElements()).to.equal(1); - }); - }); + it('should return the number of inserted batch elements', async () => { + expect(await OVM_StateCommitmentChain.getTotalElements()).to.equal(1) + }) + }) - describe("when 64 batch elements have been inserted in one batch", () => { + describe('when 64 batch elements have been inserted in one batch', () => { beforeEach(async () => { - const batch = Array(64).fill(NON_NULL_BYTES32); + const batch = Array(64).fill(NON_NULL_BYTES32) Mock__OVM_CanonicalTransactionChain.smocked.getTotalElements.will.return.with( batch.length - ); - await OVM_StateCommitmentChain.appendStateBatch(batch, 0, 0, "0x"); - }); + ) + await OVM_StateCommitmentChain.appendStateBatch(batch, 0, 0, '0x') + }) - it("should return the number of inserted batch elements", async () => { - expect(await OVM_StateCommitmentChain.getTotalElements()).to.equal(64); - }); - }); + it('should return the number of inserted batch elements', async () => { + expect(await OVM_StateCommitmentChain.getTotalElements()).to.equal(64) + }) + }) - describe("when 32 batch elements have been inserted in each of two batches", () => { + describe('when 32 batch elements have been inserted in each of two batches', () => { beforeEach(async () => { - const batch = Array(32).fill(NON_NULL_BYTES32); + const batch = Array(32).fill(NON_NULL_BYTES32) Mock__OVM_CanonicalTransactionChain.smocked.getTotalElements.will.return.with( batch.length * 2 - ); - await OVM_StateCommitmentChain.appendStateBatch(batch, 0, 0, "0x"); - await OVM_StateCommitmentChain.appendStateBatch(batch, 32, 0, "0x"); - }); - - it("should return the number of inserted batch elements", async () => { - expect(await OVM_StateCommitmentChain.getTotalElements()).to.equal(64); - }); - }); - }); - - describe("getTotalBatches()", () => { - describe("when no batches have been inserted", () => { - it("should return zero", async () => { - expect(await OVM_StateCommitmentChain.getTotalBatches()).to.equal(0); - }); - }); - - describe("when one batch has been inserted", () => { + ) + await OVM_StateCommitmentChain.appendStateBatch(batch, 0, 0, '0x') + await OVM_StateCommitmentChain.appendStateBatch(batch, 32, 0, '0x') + }) + + it('should return the number of inserted batch elements', async () => { + expect(await OVM_StateCommitmentChain.getTotalElements()).to.equal(64) + }) + }) + }) + + describe('getTotalBatches()', () => { + describe('when no batches have been inserted', () => { + it('should return zero', async () => { + expect(await OVM_StateCommitmentChain.getTotalBatches()).to.equal(0) + }) + }) + + describe('when one batch has been inserted', () => { beforeEach(async () => { - const batch = [NON_NULL_BYTES32]; + const batch = [NON_NULL_BYTES32] Mock__OVM_CanonicalTransactionChain.smocked.getTotalElements.will.return.with( batch.length - ); - await OVM_StateCommitmentChain.appendStateBatch(batch, 0, 0, "0x"); - }); + ) + await OVM_StateCommitmentChain.appendStateBatch(batch, 0, 0, '0x') + }) - it("should return the number of inserted batch elements", async () => { - expect(await OVM_StateCommitmentChain.getTotalBatches()).to.equal(1); - }); - }); + it('should return the number of inserted batch elements', async () => { + expect(await OVM_StateCommitmentChain.getTotalBatches()).to.equal(1) + }) + }) - describe("when 8 batches have been inserted", () => { + describe('when 8 batches have been inserted', () => { beforeEach(async () => { - const batch = [NON_NULL_BYTES32]; + const batch = [NON_NULL_BYTES32] Mock__OVM_CanonicalTransactionChain.smocked.getTotalElements.will.return.with( batch.length * 8 - ); + ) for (let i = 0; i < 8; i++) { - await OVM_StateCommitmentChain.appendStateBatch(batch, i, 0, "0x"); + await OVM_StateCommitmentChain.appendStateBatch(batch, i, 0, '0x') } - }); + }) - it("should return the number of inserted batch elements", async () => { - expect(await OVM_StateCommitmentChain.getTotalBatches()).to.equal(8); - }); - }); - }); + it('should return the number of inserted batch elements', async () => { + expect(await OVM_StateCommitmentChain.getTotalBatches()).to.equal(8) + }) + }) + }) - describe("verifyElement()", () => { - it("should revert when given an invalid batch header", async () => { + describe('verifyElement()', () => { + it('should revert when given an invalid batch header', async () => { // TODO - }); + }) - it("should revert when given an invalid inclusion proof", async () => { + it('should revert when given an invalid inclusion proof', async () => { // TODO - }); + }) - it("should return true when given a valid proof", async () => { + it('should return true when given a valid proof', async () => { // TODO - }); - }); -}); + }) + }) +}) diff --git a/test/contracts/OVM/verification/OVM_FraudVerifier.spec.ts b/test/contracts/OVM/verification/OVM_FraudVerifier.spec.ts index 9ad9a043b..f4bc5ba50 100644 --- a/test/contracts/OVM/verification/OVM_FraudVerifier.spec.ts +++ b/test/contracts/OVM/verification/OVM_FraudVerifier.spec.ts @@ -1,9 +1,9 @@ -import { expect } from "../../../setup"; +import { expect } from '../../../setup' /* External Imports */ -import { ethers } from "hardhat"; -import { ContractFactory, Contract, BigNumber } from "ethers"; -import { smockit, MockContract } from "@eth-optimism/smock"; +import { ethers } from 'hardhat' +import { ContractFactory, Contract, BigNumber } from 'ethers' +import { smockit, MockContract } from '@eth-optimism/smock' /* Internal Imports */ import { @@ -15,7 +15,7 @@ import { NON_NULL_BYTES32, NULL_BYTES32, hashTransaction, -} from "../../../helpers"; +} from '../../../helpers' const DUMMY_TX_CHAIN_ELEMENTS = [...Array(10).keys()].map((i) => { return { @@ -24,102 +24,102 @@ const DUMMY_TX_CHAIN_ELEMENTS = [...Array(10).keys()].map((i) => { timestamp: BigNumber.from(i), blockNumber: BigNumber.from(0), txData: NULL_BYTES32, - }; -}); + } +}) -const DUMMY_HASH = hashTransaction(DUMMY_OVM_TRANSACTIONS[0]); +const DUMMY_HASH = hashTransaction(DUMMY_OVM_TRANSACTIONS[0]) const DUMMY_BATCH_PROOFS_WITH_INDEX = [ { index: 11, siblings: [NULL_BYTES32], }, -]; +] -describe("OVM_FraudVerifier", () => { - let AddressManager: Contract; +describe('OVM_FraudVerifier', () => { + let AddressManager: Contract before(async () => { - AddressManager = await makeAddressManager(); - }); - - let Mock__OVM_StateCommitmentChain: MockContract; - let Mock__OVM_CanonicalTransactionChain: MockContract; - let Mock__OVM_StateTransitioner: MockContract; - let Mock__OVM_StateTransitionerFactory: MockContract; - let Mock__OVM_BondManager: MockContract; + AddressManager = await makeAddressManager() + }) + + let Mock__OVM_StateCommitmentChain: MockContract + let Mock__OVM_CanonicalTransactionChain: MockContract + let Mock__OVM_StateTransitioner: MockContract + let Mock__OVM_StateTransitionerFactory: MockContract + let Mock__OVM_BondManager: MockContract before(async () => { Mock__OVM_StateCommitmentChain = await smockit( - await ethers.getContractFactory("OVM_StateCommitmentChain") - ); + await ethers.getContractFactory('OVM_StateCommitmentChain') + ) Mock__OVM_CanonicalTransactionChain = await smockit( - await ethers.getContractFactory("OVM_CanonicalTransactionChain") - ); + await ethers.getContractFactory('OVM_CanonicalTransactionChain') + ) Mock__OVM_StateTransitioner = await smockit( - await ethers.getContractFactory("OVM_StateTransitioner") - ); + await ethers.getContractFactory('OVM_StateTransitioner') + ) Mock__OVM_StateTransitionerFactory = await smockit( - await ethers.getContractFactory("OVM_StateTransitionerFactory") - ); + await ethers.getContractFactory('OVM_StateTransitionerFactory') + ) Mock__OVM_BondManager = await smockit( - await ethers.getContractFactory("OVM_BondManager") - ); + await ethers.getContractFactory('OVM_BondManager') + ) await setProxyTarget( AddressManager, - "OVM_StateCommitmentChain", + 'OVM_StateCommitmentChain', Mock__OVM_StateCommitmentChain - ); + ) await setProxyTarget( AddressManager, - "OVM_CanonicalTransactionChain", + 'OVM_CanonicalTransactionChain', Mock__OVM_CanonicalTransactionChain - ); + ) await setProxyTarget( AddressManager, - "OVM_StateTransitionerFactory", + 'OVM_StateTransitionerFactory', Mock__OVM_StateTransitionerFactory - ); + ) await setProxyTarget( AddressManager, - "OVM_BondManager", + 'OVM_BondManager', Mock__OVM_BondManager - ); + ) Mock__OVM_StateTransitionerFactory.smocked.create.will.return.with( Mock__OVM_StateTransitioner.address - ); - }); + ) + }) - let Factory__OVM_FraudVerifier: ContractFactory; + let Factory__OVM_FraudVerifier: ContractFactory before(async () => { Factory__OVM_FraudVerifier = await ethers.getContractFactory( - "OVM_FraudVerifier" - ); - }); + 'OVM_FraudVerifier' + ) + }) - let OVM_FraudVerifier: Contract; + let OVM_FraudVerifier: Contract beforeEach(async () => { OVM_FraudVerifier = await Factory__OVM_FraudVerifier.deploy( AddressManager.address - ); - }); + ) + }) - describe("initializeFraudVerification", () => { - describe("when provided an invalid pre-state root inclusion proof", () => { + describe('initializeFraudVerification', () => { + describe('when provided an invalid pre-state root inclusion proof', () => { before(() => { Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with( false - ); - }); + ) + }) - it("should revert", async () => { + it('should revert', async () => { await expect( OVM_FraudVerifier.initializeFraudVerification( NULL_BYTES32, @@ -130,25 +130,25 @@ describe("OVM_FraudVerifier", () => { DUMMY_BATCH_HEADERS[0], DUMMY_BATCH_PROOFS[0] ) - ).to.be.revertedWith("Invalid pre-state root inclusion proof."); - }); - }); + ).to.be.revertedWith('Invalid pre-state root inclusion proof.') + }) + }) - describe("when provided a valid pre-state root inclusion proof", () => { + describe('when provided a valid pre-state root inclusion proof', () => { before(() => { Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with( true - ); - }); + ) + }) - describe("when provided an invalid transaction inclusion proof", () => { + describe('when provided an invalid transaction inclusion proof', () => { before(() => { Mock__OVM_CanonicalTransactionChain.smocked.verifyTransaction.will.return.with( false - ); - }); + ) + }) - it("should revert", async () => { + it('should revert', async () => { await expect( OVM_FraudVerifier.initializeFraudVerification( NULL_BYTES32, @@ -159,18 +159,18 @@ describe("OVM_FraudVerifier", () => { DUMMY_BATCH_HEADERS[0], DUMMY_BATCH_PROOFS[0] ) - ).to.be.revertedWith("Invalid transaction inclusion proof."); - }); - }); + ).to.be.revertedWith('Invalid transaction inclusion proof.') + }) + }) - describe("when provided a valid transaction inclusion proof", () => { + describe('when provided a valid transaction inclusion proof', () => { before(() => { Mock__OVM_CanonicalTransactionChain.smocked.verifyTransaction.will.return.with( true - ); - }); + ) + }) - it("should deploy a new state transitioner", async () => { + it('should deploy a new state transitioner', async () => { await expect( OVM_FraudVerifier.initializeFraudVerification( NULL_BYTES32, @@ -184,17 +184,17 @@ describe("OVM_FraudVerifier", () => { index: DUMMY_BATCH_PROOFS[0].index + 1, } ) - ).to.not.be.reverted; + ).to.not.be.reverted expect( await OVM_FraudVerifier.getStateTransitioner( NULL_BYTES32, DUMMY_HASH ) - ).to.equal(Mock__OVM_StateTransitioner.address); - }); + ).to.equal(Mock__OVM_StateTransitioner.address) + }) - it("should revert when provided with a incorrect transaction root global index", async () => { + it('should revert when provided with a incorrect transaction root global index', async () => { await expect( OVM_FraudVerifier.initializeFraudVerification( NULL_BYTES32, @@ -206,21 +206,21 @@ describe("OVM_FraudVerifier", () => { DUMMY_BATCH_PROOFS_WITH_INDEX[0] ) ).to.be.revertedWith( - "Pre-state root global index must equal to the transaction root global index." - ); - }); - }); - }); - }); - - describe("finalizeFraudVerification", () => { + 'Pre-state root global index must equal to the transaction root global index.' + ) + }) + }) + }) + }) + + describe('finalizeFraudVerification', () => { beforeEach(async () => { Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with( true - ); + ) Mock__OVM_CanonicalTransactionChain.smocked.verifyTransaction.will.return.with( true - ); + ) await OVM_FraudVerifier.initializeFraudVerification( NULL_BYTES32, @@ -233,15 +233,15 @@ describe("OVM_FraudVerifier", () => { ...DUMMY_BATCH_PROOFS[0], index: DUMMY_BATCH_PROOFS[0].index + 1, } - ); - }); + ) + }) - describe("when the transition process is not complete", () => { + describe('when the transition process is not complete', () => { before(async () => { - Mock__OVM_StateTransitioner.smocked.isComplete.will.return.with(false); - }); + Mock__OVM_StateTransitioner.smocked.isComplete.will.return.with(false) + }) - it("should revert", async () => { + it('should revert', async () => { await expect( OVM_FraudVerifier.finalizeFraudVerification( NULL_BYTES32, @@ -253,23 +253,23 @@ describe("OVM_FraudVerifier", () => { DUMMY_BATCH_PROOFS[0] ) ).to.be.revertedWith( - "State transition process must be completed prior to finalization." - ); - }); - }); + 'State transition process must be completed prior to finalization.' + ) + }) + }) - describe("when the transition process is complete", () => { + describe('when the transition process is complete', () => { before(() => { - Mock__OVM_StateTransitioner.smocked.isComplete.will.return.with(true); - }); + Mock__OVM_StateTransitioner.smocked.isComplete.will.return.with(true) + }) - describe("when provided an invalid post-state root index", () => { + describe('when provided an invalid post-state root index', () => { const batchProof = { ...DUMMY_BATCH_PROOFS[0], index: DUMMY_BATCH_PROOFS[0].index + 2, - }; + } - it("should revert", async () => { + it('should revert', async () => { await expect( OVM_FraudVerifier.finalizeFraudVerification( NULL_BYTES32, @@ -281,25 +281,25 @@ describe("OVM_FraudVerifier", () => { batchProof ) ).to.be.revertedWith( - "Post-state root global index must equal to the pre state root global index plus one." - ); - }); - }); + 'Post-state root global index must equal to the pre state root global index plus one.' + ) + }) + }) - describe("when provided a valid post-state root index", () => { + describe('when provided a valid post-state root index', () => { const batchProof = { ...DUMMY_BATCH_PROOFS[0], index: DUMMY_BATCH_PROOFS[0].index + 1, - }; + } - describe("when provided an invalid pre-state root inclusion proof", () => { + describe('when provided an invalid pre-state root inclusion proof', () => { beforeEach(() => { Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with( false - ); - }); + ) + }) - it("should revert", async () => { + it('should revert', async () => { await expect( OVM_FraudVerifier.finalizeFraudVerification( NULL_BYTES32, @@ -310,27 +310,27 @@ describe("OVM_FraudVerifier", () => { DUMMY_BATCH_HEADERS[0], batchProof ) - ).to.be.revertedWith("Invalid pre-state root inclusion proof."); - }); - }); + ).to.be.revertedWith('Invalid pre-state root inclusion proof.') + }) + }) - describe("when provided a valid pre-state root inclusion proof", () => { + describe('when provided a valid pre-state root inclusion proof', () => { before(() => { Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with( true - ); - }); + ) + }) - describe("when provided an invalid post-state root inclusion proof", () => { + describe('when provided an invalid post-state root inclusion proof', () => { beforeEach(() => { Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with( (stateRoot: string, ...args: any) => { - return stateRoot !== NON_NULL_BYTES32; + return stateRoot !== NON_NULL_BYTES32 } - ); - }); + ) + }) - it("should revert", async () => { + it('should revert', async () => { await expect( OVM_FraudVerifier.finalizeFraudVerification( NULL_BYTES32, @@ -341,25 +341,25 @@ describe("OVM_FraudVerifier", () => { DUMMY_BATCH_HEADERS[0], batchProof ) - ).to.be.revertedWith("Invalid post-state root inclusion proof."); - }); - }); + ).to.be.revertedWith('Invalid post-state root inclusion proof.') + }) + }) - describe("when provided a valid post-state root inclusion proof", () => { + describe('when provided a valid post-state root inclusion proof', () => { before(() => { Mock__OVM_StateCommitmentChain.smocked.verifyStateCommitment.will.return.with( true - ); - }); + ) + }) - describe("when the provided post-state root does not differ from the computed one", () => { + describe('when the provided post-state root does not differ from the computed one', () => { before(() => { Mock__OVM_StateTransitioner.smocked.getPostStateRoot.will.return.with( NON_NULL_BYTES32 - ); - }); + ) + }) - it("should revert", async () => { + it('should revert', async () => { await expect( OVM_FraudVerifier.finalizeFraudVerification( NULL_BYTES32, @@ -371,19 +371,19 @@ describe("OVM_FraudVerifier", () => { batchProof ) ).to.be.revertedWith( - "State transition has not been proven fraudulent." - ); - }); - }); + 'State transition has not been proven fraudulent.' + ) + }) + }) - describe("when the provided post-state root differs from the computed one", () => { + describe('when the provided post-state root differs from the computed one', () => { before(() => { Mock__OVM_StateTransitioner.smocked.getPostStateRoot.will.return.with( NULL_BYTES32 - ); - }); + ) + }) - it("should succeed and attempt to delete a state batch", async () => { + it('should succeed and attempt to delete a state batch', async () => { await OVM_FraudVerifier.finalizeFraudVerification( NULL_BYTES32, DUMMY_BATCH_HEADERS[0], @@ -392,46 +392,46 @@ describe("OVM_FraudVerifier", () => { NON_NULL_BYTES32, DUMMY_BATCH_HEADERS[0], batchProof - ); + ) const batchHeader = Object.values(DUMMY_BATCH_HEADERS[0]).map( (value) => { return Number.isInteger(value) ? BigNumber.from(value) - : value; + : value } - ); + ) expect( Mock__OVM_StateCommitmentChain.smocked.deleteStateBatch .calls[0] - ).to.deep.equal([batchHeader, BigNumber.from("0"), "0x"]); - }); - }); - }); - }); - }); - - describe("multiple fraud proofs for the same pre-execution state", () => { - let state2: any; - const DUMMY_HASH_2 = hashTransaction(DUMMY_OVM_TRANSACTIONS[1]); + ).to.deep.equal([batchHeader, BigNumber.from('0'), '0x']) + }) + }) + }) + }) + }) + + describe('multiple fraud proofs for the same pre-execution state', () => { + let state2: any + const DUMMY_HASH_2 = hashTransaction(DUMMY_OVM_TRANSACTIONS[1]) beforeEach(async () => { state2 = await smockit( - await ethers.getContractFactory("OVM_StateTransitioner") - ); + await ethers.getContractFactory('OVM_StateTransitioner') + ) Mock__OVM_StateTransitionerFactory.smocked.create.will.return.with( state2.address - ); + ) Mock__OVM_StateTransitioner.smocked.getPostStateRoot.will.return.with( NULL_BYTES32 - ); + ) - state2.smocked.getPostStateRoot.will.return.with(NULL_BYTES32); - }); + state2.smocked.getPostStateRoot.will.return.with(NULL_BYTES32) + }) - it("creates multiple state transitioners per tx hash", async () => { + it('creates multiple state transitioners per tx hash', async () => { await expect( OVM_FraudVerifier.initializeFraudVerification( NULL_BYTES32, @@ -445,29 +445,29 @@ describe("OVM_FraudVerifier", () => { index: DUMMY_BATCH_PROOFS[0].index + 1, } ) - ).to.not.be.reverted; + ).to.not.be.reverted expect( await OVM_FraudVerifier.getStateTransitioner( NULL_BYTES32, DUMMY_HASH ) - ).to.equal(Mock__OVM_StateTransitioner.address); + ).to.equal(Mock__OVM_StateTransitioner.address) expect( await OVM_FraudVerifier.getStateTransitioner( NULL_BYTES32, DUMMY_HASH_2 ) - ).to.equal(state2.address); - }); + ).to.equal(state2.address) + }) const batchProof = { ...DUMMY_BATCH_PROOFS[0], index: DUMMY_BATCH_PROOFS[0].index + 1, - }; + } // TODO: Appears to be failing because of a bug in smock. - it.skip("Case 1: allows proving fraud on the same pre-state root twice", async () => { + it.skip('Case 1: allows proving fraud on the same pre-state root twice', async () => { // finalize previous fraud await OVM_FraudVerifier.finalizeFraudVerification( NULL_BYTES32, @@ -477,7 +477,7 @@ describe("OVM_FraudVerifier", () => { NON_NULL_BYTES32, DUMMY_BATCH_HEADERS[0], batchProof - ); + ) // start new fraud await OVM_FraudVerifier.initializeFraudVerification( @@ -491,7 +491,7 @@ describe("OVM_FraudVerifier", () => { ...DUMMY_BATCH_PROOFS[0], index: DUMMY_BATCH_PROOFS[0].index + 1, } - ); + ) // finalize it as well await OVM_FraudVerifier.finalizeFraudVerification( @@ -502,20 +502,20 @@ describe("OVM_FraudVerifier", () => { NON_NULL_BYTES32, DUMMY_BATCH_HEADERS[1], batchProof - ); + ) // the new batch was deleted expect( Mock__OVM_StateCommitmentChain.smocked.deleteStateBatch.calls[0] ).to.deep.equal([ Object.values(DUMMY_BATCH_HEADERS[1]).map((value) => { - return Number.isInteger(value) ? BigNumber.from(value) : value; + return Number.isInteger(value) ? BigNumber.from(value) : value }), - ]); - }); + ]) + }) // TODO: Appears to be failing because of a bug in smock. - it.skip("Case 2: does not get blocked by the first transitioner", async () => { + it.skip('Case 2: does not get blocked by the first transitioner', async () => { // start new fraud await OVM_FraudVerifier.initializeFraudVerification( NULL_BYTES32, @@ -528,7 +528,7 @@ describe("OVM_FraudVerifier", () => { ...DUMMY_BATCH_PROOFS[0], index: DUMMY_BATCH_PROOFS[0].index + 1, } - ); + ) // finalize the new fraud first await OVM_FraudVerifier.finalizeFraudVerification( @@ -539,16 +539,16 @@ describe("OVM_FraudVerifier", () => { NON_NULL_BYTES32, DUMMY_BATCH_HEADERS[1], batchProof - ); + ) // the new fraud's batch was deleted expect( Mock__OVM_StateCommitmentChain.smocked.deleteStateBatch.calls[0] ).to.deep.equal([ Object.values(DUMMY_BATCH_HEADERS[1]).map((value) => { - return Number.isInteger(value) ? BigNumber.from(value) : value; + return Number.isInteger(value) ? BigNumber.from(value) : value }), - ]); + ]) // finalize previous fraud await OVM_FraudVerifier.finalizeFraudVerification( @@ -559,18 +559,18 @@ describe("OVM_FraudVerifier", () => { NON_NULL_BYTES32, DUMMY_BATCH_HEADERS[0], batchProof - ); + ) // the old fraud's batch was deleted expect( Mock__OVM_StateCommitmentChain.smocked.deleteStateBatch.calls[0] ).to.deep.equal([ Object.values(DUMMY_BATCH_HEADERS[0]).map((value) => { - return Number.isInteger(value) ? BigNumber.from(value) : value; + return Number.isInteger(value) ? BigNumber.from(value) : value }), - ]); - }); - }); - }); - }); -}); + ]) + }) + }) + }) + }) +})