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..9e8188a88 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -29,7 +29,7 @@ const config: HardhatUserConfig = { }, }, typechain: { - outDir: 'build/types', + outDir: 'src/types', target: 'ethers-v5', }, } diff --git a/test/contracts/OVM/bridge/base/OVM_L1CrossDomainMessenger.spec.ts b/test/contracts/OVM/bridge/base/OVM_L1CrossDomainMessenger.spec.ts index 1f9ce6741..597803c9f 100644 --- a/test/contracts/OVM/bridge/base/OVM_L1CrossDomainMessenger.spec.ts +++ b/test/contracts/OVM/bridge/base/OVM_L1CrossDomainMessenger.spec.ts @@ -109,12 +109,19 @@ describe('OVM_L1CrossDomainMessenger', () => { OVM_L1CrossDomainMessenger.sendMessage(target, message, gasLimit) ).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', ]) }) diff --git a/test/contracts/OVM/chain/OVM_CanonicalTransactionChain.spec.ts b/test/contracts/OVM/chain/OVM_CanonicalTransactionChain.spec.ts index 440b94330..38c717f90 100644 --- a/test/contracts/OVM/chain/OVM_CanonicalTransactionChain.spec.ts +++ b/test/contracts/OVM/chain/OVM_CanonicalTransactionChain.spec.ts @@ -109,6 +109,7 @@ const encodeAppendSequencerBatch = (b: AppendSequencerBatchParams): string => { const encodedTxDataHeader = remove0x( BigNumber.from(remove0x(cur).length / 2).toHexString() ).padStart(6, '0') + return acc + encodedTxDataHeader + remove0x(cur) }, '') @@ -252,7 +253,7 @@ describe('OVM_CanonicalTransactionChain', () => { 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.' ) @@ -262,7 +263,13 @@ describe('OVM_CanonicalTransactionChain', () => { 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.' ) @@ -274,7 +281,13 @@ describe('OVM_CanonicalTransactionChain', () => { const data = '0x' + '12'.repeat(1234) await expect( - OVM_CanonicalTransactionChain.enqueue(target, customGasLimit, data) + OVM_CanonicalTransactionChain.enqueue( + target, + customGasLimit, + data, + 0, + '0x' + ) ).to.be.revertedWith('Transaction gas limit too low to enqueue.') }) @@ -283,7 +296,7 @@ describe('OVM_CanonicalTransactionChain', () => { 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.') @@ -297,7 +310,7 @@ describe('OVM_CanonicalTransactionChain', () => { await setEthTime(ethers.provider, timestamp) await expect( - OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data) + OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data, 0, '0x') ).to.emit(OVM_CanonicalTransactionChain, 'TransactionEnqueued') }) @@ -308,7 +321,13 @@ describe('OVM_CanonicalTransactionChain', () => { it(`should be able to enqueue ${size} elements`, async () => { for (let i = 0; i < size; i++) { await expect( - OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data) + OVM_CanonicalTransactionChain.enqueue( + target, + gasLimit, + data, + 0, + '0x' + ) ).to.not.be.reverted } }) @@ -343,13 +362,21 @@ describe('OVM_CanonicalTransactionChain', () => { 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' ) } @@ -390,13 +417,17 @@ describe('OVM_CanonicalTransactionChain', () => { 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' ) } } @@ -437,13 +468,17 @@ describe('OVM_CanonicalTransactionChain', () => { 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' ) } } @@ -466,7 +501,7 @@ describe('OVM_CanonicalTransactionChain', () => { describe('appendQueueBatch disabled', () => { it('should revert', async () => { await expect( - OVM_CanonicalTransactionChain.appendQueueBatch(0) + OVM_CanonicalTransactionChain.appendQueueBatch(0, 0, '0x') ).to.be.revertedWith('appendQueueBatch is currently disabled.') }) }) @@ -474,13 +509,13 @@ describe('OVM_CanonicalTransactionChain', () => { describe.skip('appendQueueBatch', () => { it('should revert if trying to append zero transactions', async () => { await expect( - OVM_CanonicalTransactionChain.appendQueueBatch(0) + OVM_CanonicalTransactionChain.appendQueueBatch(0, 0, '0x') ).to.be.revertedWith('Must append more than zero transactions.') }) it('should revert if the queue is empty', async () => { await expect( - OVM_CanonicalTransactionChain.appendQueueBatch(1) + OVM_CanonicalTransactionChain.appendQueueBatch(1, 0, '0x') ).to.be.revertedWith('Must append more than zero transactions.') }) @@ -496,7 +531,9 @@ describe('OVM_CanonicalTransactionChain', () => { await OVM_CanonicalTransactionChain.enqueue( target, gasLimit, - data + data, + 0, + '0x' ) } }) @@ -505,7 +542,9 @@ describe('OVM_CanonicalTransactionChain', () => { 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.' @@ -516,7 +555,7 @@ describe('OVM_CanonicalTransactionChain', () => { await expect( OVM_CanonicalTransactionChain.connect( sequencer - ).appendQueueBatch(1) + ).appendQueueBatch(1, 0, '0x') ) .to.emit(OVM_CanonicalTransactionChain, 'QueueBatchAppended') .withArgs(0, 1, 1) @@ -532,20 +571,28 @@ describe('OVM_CanonicalTransactionChain', () => { }) it('should be able to append a single element', async () => { - await expect(OVM_CanonicalTransactionChain.appendQueueBatch(1)) + 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)) + 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) @@ -564,7 +611,14 @@ describe('OVM_CanonicalTransactionChain', () => { const timestamp = (await getEthTime(ethers.provider)) + 100 await setEthTime(ethers.provider, timestamp) - await OVM_CanonicalTransactionChain.enqueue(entrypoint, gasLimit, data) + + await OVM_CanonicalTransactionChain.enqueue( + entrypoint, + gasLimit, + data, + 0, + '0x' + ) const blockNumber = await ethers.provider.getBlockNumber() @@ -625,12 +679,18 @@ describe('OVM_CanonicalTransactionChain', () => { const timestamp = (await getEthTime(ethers.provider)) + 100 await setEthTime(ethers.provider, timestamp) - await OVM_CanonicalTransactionChain.enqueue(entrypoint, gasLimit, data) + 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) + await OVM_CanonicalTransactionChain.appendQueueBatch(1, 0, '0x') expect( await OVM_CanonicalTransactionChain.verifyTransaction( @@ -908,7 +968,13 @@ describe('OVM_CanonicalTransactionChain', () => { 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( @@ -996,7 +1062,9 @@ describe('OVM_CanonicalTransactionChain', () => { await OVM_CanonicalTransactionChain.enqueue( target, gasLimit, - data + data, + 0, + '0x' ) }) it('should revert if the first context timestamp is > the head queue element timestamp', async () => { @@ -1056,7 +1124,9 @@ describe('OVM_CanonicalTransactionChain', () => { await OVM_CanonicalTransactionChain.enqueue( target, gasLimit, - data + data, + 0, + '0x' ) queueElements[ i @@ -1311,7 +1381,13 @@ describe('OVM_CanonicalTransactionChain', () => { // enqueue timestamp = await getEthTime(ethers.provider) blockNumber = await getNextBlockNumber(ethers.provider) - await OVM_CanonicalTransactionChain.enqueue(target, gasLimit, data) + await OVM_CanonicalTransactionChain.enqueue( + target, + gasLimit, + data, + 0, + '0x' + ) await appendSequencerBatch(OVM_CanonicalTransactionChain, { transactions: ['0x1234'], contexts: [ @@ -1371,7 +1447,13 @@ describe('OVM_CanonicalTransactionChain', () => { 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, @@ -1455,7 +1537,9 @@ describe('OVM_CanonicalTransactionChain', () => { await OVM_CanonicalTransactionChain.enqueue( target, gasLimit, - data + data, + 0, + '0x' ) } }) diff --git a/test/contracts/OVM/chain/OVM_StateCommitmentChain.spec.ts b/test/contracts/OVM/chain/OVM_StateCommitmentChain.spec.ts index 7e4a26264..8cb6b3dfc 100644 --- a/test/contracts/OVM/chain/OVM_StateCommitmentChain.spec.ts +++ b/test/contracts/OVM/chain/OVM_StateCommitmentChain.spec.ts @@ -101,7 +101,7 @@ describe('OVM_StateCommitmentChain', () => { it('should revert', async () => { await expect( - OVM_StateCommitmentChain.appendStateBatch(batch, 0) + OVM_StateCommitmentChain.appendStateBatch(batch, 0, 0, '0x') ).to.be.revertedWith('Cannot submit an empty state batch.') }) }) @@ -112,7 +112,7 @@ describe('OVM_StateCommitmentChain', () => { 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.' ) @@ -128,7 +128,7 @@ describe('OVM_StateCommitmentChain', () => { 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.' ) @@ -143,8 +143,9 @@ describe('OVM_StateCommitmentChain', () => { }) it('should append the state batch', async () => { - await expect(OVM_StateCommitmentChain.appendStateBatch(batch, 0)).to - .not.be.reverted + await expect( + OVM_StateCommitmentChain.appendStateBatch(batch, 0, 0, '0x') + ).to.not.be.reverted }) }) @@ -156,14 +157,21 @@ describe('OVM_StateCommitmentChain', () => { await OVM_StateCommitmentChain.connect(sequencer).appendStateBatch( batch, - 0 + 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.' ) @@ -181,7 +189,12 @@ describe('OVM_StateCommitmentChain', () => { it('should succeed', async () => { await expect( - OVM_StateCommitmentChain.connect(user).appendStateBatch(batch, 1) + OVM_StateCommitmentChain.connect(user).appendStateBatch( + batch, + 1, + 0, + '0x' + ) ).to.not.be.reverted }) }) @@ -203,7 +216,7 @@ describe('OVM_StateCommitmentChain', () => { 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'], [await getEthTime(ethers.provider), await sequencer.getAddress()] @@ -217,7 +230,7 @@ describe('OVM_StateCommitmentChain', () => { 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.' ) @@ -235,10 +248,14 @@ describe('OVM_StateCommitmentChain', () => { describe('when the provided batch index is greater than the total submitted', () => { it('should revert', async () => { await expect( - OVM_StateCommitmentChain.deleteStateBatch({ - ...batchHeader, - batchIndex: 1, - }) + OVM_StateCommitmentChain.deleteStateBatch( + { + ...batchHeader, + batchIndex: 1, + }, + 0, + '0x' + ) ).to.be.revertedWith('Index out of bounds.') }) }) @@ -247,18 +264,23 @@ describe('OVM_StateCommitmentChain', () => { describe('when the provided batch header is invalid', () => { it('should revert', async () => { await expect( - OVM_StateCommitmentChain.deleteStateBatch({ - ...batchHeader, - extraData: '0x' + '22'.repeat(32), - }) + 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)) - .to.not.be.reverted + await expect( + OVM_StateCommitmentChain.deleteStateBatch(batchHeader, 0, '0x') + ).to.not.be.reverted }) }) }) @@ -278,7 +300,7 @@ describe('OVM_StateCommitmentChain', () => { 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 () => { @@ -292,7 +314,7 @@ describe('OVM_StateCommitmentChain', () => { 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 () => { @@ -306,8 +328,8 @@ describe('OVM_StateCommitmentChain', () => { Mock__OVM_CanonicalTransactionChain.smocked.getTotalElements.will.return.with( batch.length * 2 ) - await OVM_StateCommitmentChain.appendStateBatch(batch, 0) - await OVM_StateCommitmentChain.appendStateBatch(batch, 32) + 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 () => { @@ -329,7 +351,7 @@ describe('OVM_StateCommitmentChain', () => { 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 () => { @@ -345,7 +367,7 @@ describe('OVM_StateCommitmentChain', () => { ) for (let i = 0; i < 8; i++) { - await OVM_StateCommitmentChain.appendStateBatch(batch, i) + await OVM_StateCommitmentChain.appendStateBatch(batch, i, 0, '0x') } }) diff --git a/test/contracts/OVM/verification/OVM_FraudVerifier.spec.ts b/test/contracts/OVM/verification/OVM_FraudVerifier.spec.ts index 8d8bc8c6c..f4bc5ba50 100644 --- a/test/contracts/OVM/verification/OVM_FraudVerifier.spec.ts +++ b/test/contracts/OVM/verification/OVM_FraudVerifier.spec.ts @@ -394,16 +394,18 @@ describe('OVM_FraudVerifier', () => { batchProof ) - expect( - Mock__OVM_StateCommitmentChain.smocked.deleteStateBatch - .calls[0] - ).to.deep.equal([ - Object.values(DUMMY_BATCH_HEADERS[0]).map((value) => { + 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([batchHeader, BigNumber.from('0'), '0x']) }) }) })