Commit faa0086a authored by Karl Floersch's avatar Karl Floersch

Remove old L1ToL2 queue

parent a71ff5bd
......@@ -10,7 +10,7 @@ import { Lib_BytesUtils } from "../../libraries/utils/Lib_BytesUtils.sol";
/* Interface Imports */
import { iOVM_L1CrossDomainMessenger } from "../../iOVM/bridge/iOVM_L1CrossDomainMessenger.sol";
import { iOVM_L1ToL2TransactionQueue } from "../../iOVM/queue/iOVM_L1ToL2TransactionQueue.sol";
import { iOVM_CanonicalTransactionChain } from "../../iOVM/chain/iOVM_CanonicalTransactionChain.sol";
import { iOVM_StateCommitmentChain } from "../../iOVM/chain/iOVM_StateCommitmentChain.sol";
/* Contract Imports */
......@@ -25,7 +25,7 @@ contract OVM_L1CrossDomainMessenger is iOVM_L1CrossDomainMessenger, OVM_BaseCros
* Contract Variables: Contract References *
*******************************************/
iOVM_L1ToL2TransactionQueue internal ovmL1ToL2TransactionQueue;
iOVM_CanonicalTransactionChain internal ovmCanonicalTransactionChain;
iOVM_StateCommitmentChain internal ovmStateCommitmentChain;
......@@ -41,7 +41,7 @@ contract OVM_L1CrossDomainMessenger is iOVM_L1CrossDomainMessenger, OVM_BaseCros
)
Lib_AddressResolver(_libAddressManager)
{
ovmL1ToL2TransactionQueue = iOVM_L1ToL2TransactionQueue(resolve("OVM_L1ToL2TransactionQueue"));
ovmCanonicalTransactionChain = iOVM_CanonicalTransactionChain(resolve("OVM_CanonicalTransactionChain"));
ovmStateCommitmentChain = iOVM_StateCommitmentChain(resolve("OVM_StateCommitmentChain"));
}
......@@ -235,7 +235,7 @@ contract OVM_L1CrossDomainMessenger is iOVM_L1CrossDomainMessenger, OVM_BaseCros
override
internal
{
ovmL1ToL2TransactionQueue.enqueue(
ovmCanonicalTransactionChain.enqueue(
targetMessengerAddress,
_gasLimit,
_message
......
......@@ -11,7 +11,6 @@ import { console } from "@nomiclabs/buidler/console.sol";
/* Interface Imports */
import { iOVM_CanonicalTransactionChain } from "../../iOVM/chain/iOVM_CanonicalTransactionChain.sol";
import { iOVM_L1ToL2TransactionQueue } from "../../iOVM/queue/iOVM_L1ToL2TransactionQueue.sol";
/* Contract Imports */
import { OVM_BaseChain } from "./OVM_BaseChain.sol";
......@@ -50,8 +49,6 @@ contract NEW_OVM_CanonicalTransactionChain is OVM_BaseChain, Lib_AddressResolver
* Contract Variables: Contract References *
*******************************************/
iOVM_L1ToL2TransactionQueue internal ovmL1ToL2TransactionQueue;
/*******************************************
* Contract Variables: Internal Accounting *
......@@ -76,7 +73,6 @@ contract NEW_OVM_CanonicalTransactionChain is OVM_BaseChain, Lib_AddressResolver
)
Lib_AddressResolver(_libAddressManager)
{
ovmL1ToL2TransactionQueue = iOVM_L1ToL2TransactionQueue(resolve("OVM_L1ToL2TransactionQueue"));
sequencerAddress = resolve("OVM_Sequencer");
forceInclusionPeriodSeconds = _forceInclusionPeriodSeconds;
queue.init(100, 50, 0); // TODO: Update once we have arbitrary condition
......@@ -106,7 +102,7 @@ contract NEW_OVM_CanonicalTransactionChain is OVM_BaseChain, Lib_AddressResolver
);
require(_gasLimit >= 20000, "Gas limit too low.");
// Consume l1 gas to pay for l2 gas on l1
// Consume l1 gas rate limit queued transactions
uint gasToConsume = _gasLimit/L2_GAS_DISCOUNT_DIVISOR;
uint startingGas = gasleft();
uint i;
......@@ -115,13 +111,13 @@ contract NEW_OVM_CanonicalTransactionChain is OVM_BaseChain, Lib_AddressResolver
}
Lib_OVMCodec.QueueElement memory element = Lib_OVMCodec.QueueElement({
timestamp: block.timestamp,
timestamp: uint40(block.timestamp),
blockNumber: uint32(block.number),
batchRoot: keccak256(abi.encodePacked(
_target,
_gasLimit,
_data
)),
isL1ToL2Batch: true
))
});
// bytes32 extraData = timestamp + blockNumber
......@@ -159,12 +155,11 @@ contract NEW_OVM_CanonicalTransactionChain is OVM_BaseChain, Lib_AddressResolver
"Function can only be called by the Sequencer."
);
if (ovmL1ToL2TransactionQueue.size() > 0) {
require(
block.timestamp < ovmL1ToL2TransactionQueue.peek().timestamp + forceInclusionPeriodSeconds,
"Older queue batches must be processed before a new sequencer batch."
);
}
// TODO: Verify that there are no outstanding queue transactions which need to be processed
// require(
// block.timestamp < queue.get(lastQueueIndex).timestamp + forceInclusionPeriodSeconds,
// "Older queue batches must be processed before a new sequencer batch."
// );
// Initialize an array which will contain the leaves of the merkle tree commitment
bytes32[] memory leaves = new bytes32[](_totalElementsToAppend);
......@@ -196,14 +191,15 @@ contract NEW_OVM_CanonicalTransactionChain is OVM_BaseChain, Lib_AddressResolver
for (uint queueTxIndex = 0; queueTxIndex < numQueuedTransactions; queueTxIndex++) {
TransactionChainElement memory element = TransactionChainElement({
isSequenced: false,
queueIndex: ovmL1ToL2TransactionQueue.size(),
queueIndex: queue.getLength(),
timestamp: 0,
blocknumber: 0,
txData: hex""
});
leaves[transactionIndex] = _hashTransactionChainElement(element);
transactionIndex++;
ovmL1ToL2TransactionQueue.dequeue();
// TODO: Increment our lastQueueIndex
// lastQueueIndex++;
}
}
......
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
import { Lib_MerkleUtils } from "../../libraries/utils/Lib_MerkleUtils.sol";
/* Interface Imports */
import { iOVM_CanonicalTransactionChain } from "../../iOVM/chain/iOVM_CanonicalTransactionChain.sol";
import { iOVM_L1ToL2TransactionQueue } from "../../iOVM/queue/iOVM_L1ToL2TransactionQueue.sol";
/* Contract Imports */
import { OVM_BaseChain } from "./OVM_BaseChain.sol";
/**
* @title OVM_CanonicalTransactionChain
*/
contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_BaseChain, Lib_AddressResolver {
/*******************************************
* Contract Variables: Contract References *
*******************************************/
iOVM_L1ToL2TransactionQueue internal ovmL1ToL2TransactionQueue;
/*******************************************
* Contract Variables: Internal Accounting *
*******************************************/
uint256 internal forceInclusionPeriodSeconds;
uint256 internal lastOVMTimestamp;
/***************
* Constructor *
***************/
/**
* @param _libAddressManager Address of the Address Manager.
* @param _forceInclusionPeriodSeconds Period during which only the sequencer can submit.
*/
constructor(
address _libAddressManager,
uint256 _forceInclusionPeriodSeconds
)
Lib_AddressResolver(_libAddressManager)
{
ovmL1ToL2TransactionQueue = iOVM_L1ToL2TransactionQueue(resolve("OVM_L1ToL2TransactionQueue"));
forceInclusionPeriodSeconds = _forceInclusionPeriodSeconds;
}
/****************************************
* Public Functions: Batch Manipulation *
****************************************/
/**
* Appends a batch from the L1ToL2TransactionQueue.
*/
function appendQueueBatch()
override
public
{
require(
ovmL1ToL2TransactionQueue.size() > 0,
"No batches are currently queued to be appended."
);
Lib_OVMCodec.QueueElement memory queueElement = ovmL1ToL2TransactionQueue.peek();
require(
queueElement.timestamp + forceInclusionPeriodSeconds <= block.timestamp,
"Cannot append until the inclusion delay period has elapsed."
);
_appendQueueBatch(queueElement, 1);
ovmL1ToL2TransactionQueue.dequeue();
}
/**
* Appends a sequencer batch.
* @param _batch Batch of transactions to append.
* @param _timestamp Timestamp for the provided batch.
*/
function appendSequencerBatch(
bytes[] memory _batch,
uint256 _timestamp
)
override
public
{
require(
msg.sender == resolve("Sequencer"),
"Function can only be called by the Sequencer."
);
require(
_batch.length > 0,
"Cannot submit an empty batch."
);
require(
_timestamp > lastOVMTimestamp,
"Batch timestamp must be later than the last OVM timestamp."
);
if (ovmL1ToL2TransactionQueue.size() > 0) {
require(
_timestamp <= ovmL1ToL2TransactionQueue.peek().timestamp,
"Older queue batches must be processed before a newer sequencer batch."
);
}
Lib_OVMCodec.QueueElement memory queueElement = Lib_OVMCodec.QueueElement({
timestamp: _timestamp,
batchRoot: Lib_MerkleUtils.getMerkleRoot(_batch),
isL1ToL2Batch: false
});
_appendQueueBatch(queueElement, _batch.length);
}
/******************************************
* Internal Functions: Batch Manipulation *
******************************************/
/**
* Appends a queue batch to the chain.
* @param _queueElement Queue element to append.
* @param _batchSize Number of elements in the batch.
*/
function _appendQueueBatch(
Lib_OVMCodec.QueueElement memory _queueElement,
uint256 _batchSize
)
internal
{
Lib_OVMCodec.ChainBatchHeader memory batchHeader = Lib_OVMCodec.ChainBatchHeader({
batchIndex: getTotalBatches(),
batchRoot: _queueElement.batchRoot,
batchSize: _batchSize,
prevTotalElements: getTotalElements(),
extraData: abi.encodePacked(
_queueElement.timestamp,
_queueElement.isL1ToL2Batch
)
});
_appendBatch(batchHeader);
lastOVMTimestamp = _queueElement.timestamp;
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
/* Interface Imports */
import { iOVM_BaseQueue } from "../../iOVM/queue/iOVM_BaseQueue.sol";
/**
* @title OVM_BaseQueue
*/
contract OVM_BaseQueue is iOVM_BaseQueue {
/****************************************
* Contract Variables: Internal Storage *
****************************************/
Lib_OVMCodec.QueueElement[] internal queue;
uint256 internal front;
/**********************
* Function Modifiers *
**********************/
/**
* Asserts that the queue is not empty.
*/
modifier notEmpty() {
require(
size() > 0,
"Queue is empty."
);
_;
}
/**********************************
* Public Functions: Queue Access *
**********************************/
/**
* Gets the size of the queue.
* @return _size Number of elements in the queue.
*/
function size()
override
public
view
returns (
uint256 _size
)
{
return front >= queue.length ? 0 : queue.length - front;
}
/**
* Gets the top element of the queue.
* @return _element First element in the queue.
*/
function peek()
override
public
view
notEmpty
returns (
Lib_OVMCodec.QueueElement memory _element
)
{
return queue[front];
}
/******************************************
* Internal Functions: Queue Manipulation *
******************************************/
/**
* Adds an element to the queue.
* @param _element Queue element to add to the queue.
*/
function _enqueue(
Lib_OVMCodec.QueueElement memory _element
)
internal
{
queue.push(_element);
}
/**
* Pops an element from the queue.
* @return _element Queue element popped from the queue.
*/
function _dequeue()
internal
notEmpty
returns (
Lib_OVMCodec.QueueElement memory _element
)
{
_element = queue[front];
delete queue[front];
front += 1;
return _element;
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
/* Interface Imports */
import { iOVM_L1ToL2TransactionQueue } from "../../iOVM/queue/iOVM_L1ToL2TransactionQueue.sol";
/* Contract Imports */
import { OVM_BaseQueue } from "./OVM_BaseQueue.sol";
/**
* @title OVM_L1ToL2TransactionQueue
*/
contract OVM_L1ToL2TransactionQueue is iOVM_L1ToL2TransactionQueue, OVM_BaseQueue, Lib_AddressResolver {
/*************************************************
* Contract Variables: Transaction Restrinctions *
*************************************************/
uint constant MAX_ROLLUP_TX_SIZE = 10000;
uint constant L2_GAS_DISCOUNT_DIVISOR = 10;
/*******************************************
* Contract Variables: Contract References *
*******************************************/
address internal ovmCanonicalTransactionChain;
/***************
* Constructor *
***************/
/**
* @param _libAddressManager Address of the Address Manager.
*/
constructor(
address _libAddressManager
)
Lib_AddressResolver(_libAddressManager)
{
ovmCanonicalTransactionChain = resolve("OVM_CanonicalTransactionChain");
}
/****************************************
* Public Functions: Queue Manipulation *
****************************************/
/**
* Adds a transaction to the queue.
* @param _target Target contract to send the transaction to.
* @param _gasLimit Gas limit for the given transaction.
* @param _data Transaction data.
*/
function enqueue(
address _target,
uint256 _gasLimit,
bytes memory _data
)
override
public
{
require(
_data.length <= MAX_ROLLUP_TX_SIZE,
"Transaction exceeds maximum rollup data size."
);
require(_gasLimit >= 20000, "Gas limit too low.");
consumeGas(_gasLimit/L2_GAS_DISCOUNT_DIVISOR);
Lib_OVMCodec.QueueElement memory element = Lib_OVMCodec.QueueElement({
timestamp: block.timestamp,
batchRoot: keccak256(abi.encodePacked(
_target,
_gasLimit,
_data
)),
isL1ToL2Batch: true
});
_enqueue(element);
}
/**
* Pops an element from the queue.
*/
function dequeue()
override
public
{
require(
msg.sender == ovmCanonicalTransactionChain,
"Sender is not allowed to dequeue."
);
_dequeue();
}
/***************************************
* External Functions: Gas Consumption *
***************************************/
/**
* Consumes all gas provided.
*/
function consumeGas
(
uint gasToConsume
)
internal
{
uint startingGas = gasleft();
uint i;
while(startingGas - gasleft() > gasToConsume) {
i++; // TODO: Replace this dumb work with minting gas token.
}
}
}
......@@ -14,6 +14,7 @@ interface iOVM_CanonicalTransactionChain is iOVM_BaseChain {
* Public Functions: Batch Manipulation *
****************************************/
function enqueue(address _target, uint256 _gasLimit, bytes memory _data) external;
function appendQueueBatch() external;
function appendSequencerBatch(bytes[] calldata _batch, uint256 _timestamp) external;
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
/**
* @title iOVM_BaseQueue
*/
interface iOVM_BaseQueue {
/**********************************
* Public Functions: Queue Access *
**********************************/
function size() external view returns (uint256 _size);
function peek() external view returns (Lib_OVMCodec.QueueElement memory _element);
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
/* Interface Imports */
import { iOVM_BaseQueue } from "./iOVM_BaseQueue.sol";
/**
* @title iOVM_L1ToL2TransactionQueue
*/
interface iOVM_L1ToL2TransactionQueue is iOVM_BaseQueue {
/****************************************
* Public Functions: Queue Manipulation *
****************************************/
function enqueue(address _target, uint256 _gasLimit, bytes memory _data) external;
function dequeue() external;
}
......@@ -82,8 +82,8 @@ library Lib_OVMCodec {
struct QueueElement {
bytes32 batchRoot;
uint256 timestamp; // TODO: make smaller so it gets back and update L1toL2queue to truncate
bool isL1ToL2Batch;
uint40 timestamp;
uint32 blockNumber;
}
struct EOATransaction {
......
import { expect } from '../../../setup'
/* External Imports */
import { ethers } from '@nomiclabs/buidler'
import { Signer, ContractFactory, Contract } from 'ethers'
import { smockit, MockContract } from '@eth-optimism/smock'
/* Internal Imports */
import {
makeAddressManager,
setProxyTarget,
getEthTime,
setEthTime,
NON_NULL_BYTES32,
FORCE_INCLUSION_PERIOD_SECONDS,
ZERO_ADDRESS,
} from '../../../helpers'
describe('OVM_CanonicalTransactionChain', () => {
let signer: Signer
before(async () => {
;[signer] = await ethers.getSigners()
})
let AddressManager: Contract
before(async () => {
AddressManager = await makeAddressManager()
})
let Mock__OVM_L1ToL2TransactionQueue: MockContract
before(async () => {
Mock__OVM_L1ToL2TransactionQueue = smockit(
await ethers.getContractFactory('OVM_L1ToL2TransactionQueue')
)
await setProxyTarget(
AddressManager,
'OVM_L1ToL2TransactionQueue',
Mock__OVM_L1ToL2TransactionQueue
)
})
let Factory__OVM_CanonicalTransactionChain: ContractFactory
before(async () => {
Factory__OVM_CanonicalTransactionChain = await ethers.getContractFactory(
'OVM_CanonicalTransactionChain'
)
})
let OVM_CanonicalTransactionChain: Contract
beforeEach(async () => {
OVM_CanonicalTransactionChain = await Factory__OVM_CanonicalTransactionChain.deploy(
AddressManager.address,
FORCE_INCLUSION_PERIOD_SECONDS
)
})
describe('appendQueueBatch()', () => {
describe('when the L1ToL2TransactionQueue queue is empty', () => {
before(() => {
Mock__OVM_L1ToL2TransactionQueue.smocked.size.will.return.with(0)
})
it('should revert', async () => {
await expect(
OVM_CanonicalTransactionChain.appendQueueBatch()
).to.be.revertedWith('No batches are currently queued to be appended.')
})
})
describe('when the L1ToL2TransactionQueue queue is not empty', () => {
before(() => {
Mock__OVM_L1ToL2TransactionQueue.smocked.size.will.return.with(1)
})
describe('when the inclusion delay period has not elapsed', () => {
beforeEach(async () => {
const timestamp = await getEthTime(ethers.provider)
Mock__OVM_L1ToL2TransactionQueue.smocked.peek.will.return.with([
{
timestamp,
batchRoot: NON_NULL_BYTES32,
isL1ToL2Batch: true,
},
])
await setEthTime(
ethers.provider,
timestamp + FORCE_INCLUSION_PERIOD_SECONDS / 2
)
})
it('should revert', async () => {
await expect(
OVM_CanonicalTransactionChain.appendQueueBatch()
).to.be.revertedWith(
'Cannot append until the inclusion delay period has elapsed.'
)
})
})
describe('when the inclusion delay period has elapsed', () => {
beforeEach(async () => {
const timestamp = await getEthTime(ethers.provider)
Mock__OVM_L1ToL2TransactionQueue.smocked.dequeue.will.return()
Mock__OVM_L1ToL2TransactionQueue.smocked.peek.will.return.with([
{
timestamp,
batchRoot: NON_NULL_BYTES32,
isL1ToL2Batch: true,
},
])
await setEthTime(
ethers.provider,
timestamp + FORCE_INCLUSION_PERIOD_SECONDS
)
})
it('should append the top element of the queue and attempt to dequeue', async () => {
await expect(OVM_CanonicalTransactionChain.appendQueueBatch()).to.not
.be.reverted
// TODO: Check that the batch root was inserted.
expect(
Mock__OVM_L1ToL2TransactionQueue.smocked.dequeue.calls.length
).to.equal(1)
})
})
})
})
describe('appendSequencerBatch()', () => {
describe('when the sender is not the sequencer', () => {
before(async () => {
await AddressManager.setAddress('Sequencer', ZERO_ADDRESS)
})
it('should revert', async () => {
await expect(
OVM_CanonicalTransactionChain.appendSequencerBatch([], 0)
).to.be.revertedWith('Function can only be called by the Sequencer.')
})
})
describe('when the sender is the sequencer', () => {
before(async () => {
await AddressManager.setAddress('Sequencer', await signer.getAddress())
})
describe('when the given batch is empty', () => {
const batch = []
it('should revert', async () => {
await expect(
OVM_CanonicalTransactionChain.appendSequencerBatch(batch, 0)
).to.be.revertedWith('Cannot submit an empty batch.')
})
})
describe('when the given batch is not empty', () => {
const batch = [NON_NULL_BYTES32]
describe('when the timestamp is not greater than the previous OVM timestamp', () => {
const timestamp = 0
it('should revert', async () => {
await expect(
OVM_CanonicalTransactionChain.appendSequencerBatch(
batch,
timestamp
)
).to.be.revertedWith(
'Batch timestamp must be later than the last OVM timestamp.'
)
})
})
describe('when the timestamp is greater than the previous OVM timestamp', () => {
const timestamp = 1000
describe('when the queue is not empty', () => {
before(() => {
Mock__OVM_L1ToL2TransactionQueue.smocked.size.will.return.with(1)
})
describe('when the first element in the queue is older than the provided batch', () => {
before(() => {
Mock__OVM_L1ToL2TransactionQueue.smocked.peek.will.return.with([
{
timestamp: timestamp / 2,
batchRoot: NON_NULL_BYTES32,
isL1ToL2Batch: true,
},
])
})
it('should revert', async () => {
await expect(
OVM_CanonicalTransactionChain.appendSequencerBatch(
batch,
timestamp
)
).to.be.revertedWith(
'Older queue batches must be processed before a newer sequencer batch.'
)
})
})
describe('when the first element in the queue is not older than the provided batch', () => {
before(() => {
Mock__OVM_L1ToL2TransactionQueue.smocked.peek.will.return.with([
{
timestamp,
batchRoot: NON_NULL_BYTES32,
isL1ToL2Batch: true,
},
])
})
it('should insert the sequencer batch', async () => {
await expect(
OVM_CanonicalTransactionChain.appendSequencerBatch(
batch,
timestamp
)
).to.not.be.reverted
// TODO: Check that the batch was inserted correctly.
})
})
})
describe('when the queue is empty', async () => {
before(() => {
Mock__OVM_L1ToL2TransactionQueue.smocked.size.will.return.with(0)
})
it('should insert the sequencer batch', async () => {
await expect(
OVM_CanonicalTransactionChain.appendSequencerBatch(
batch,
timestamp
)
).to.not.be.reverted
// TODO: Check that the batch was inserted correctly.
})
})
})
})
})
})
describe('getTotalElements()', () => {
describe('when no batch elements have been inserted', () => {
it('should return zero', async () => {
expect(await OVM_CanonicalTransactionChain.getTotalElements()).to.equal(
0
)
})
})
describe('when one batch element has been inserted', () => {
beforeEach(async () => {
Mock__OVM_L1ToL2TransactionQueue.smocked.size.will.return.with(0)
await OVM_CanonicalTransactionChain.appendSequencerBatch(
[NON_NULL_BYTES32],
1000
)
})
it('should return the number of inserted batch elements', async () => {
expect(await OVM_CanonicalTransactionChain.getTotalElements()).to.equal(
1
)
})
})
describe('when 64 batch elements have been inserted in one batch', () => {
const batch = Array(64).fill(NON_NULL_BYTES32)
beforeEach(async () => {
Mock__OVM_L1ToL2TransactionQueue.smocked.size.will.return.with(0)
await OVM_CanonicalTransactionChain.appendSequencerBatch(batch, 1000)
})
it('should return the number of inserted batch elements', async () => {
expect(await OVM_CanonicalTransactionChain.getTotalElements()).to.equal(
64
)
})
})
describe('when 32 batch elements have been inserted in each of two batches', () => {
const batch = Array(32).fill(NON_NULL_BYTES32)
beforeEach(async () => {
Mock__OVM_L1ToL2TransactionQueue.smocked.size.will.return.with(0)
await OVM_CanonicalTransactionChain.appendSequencerBatch(batch, 1000)
await OVM_CanonicalTransactionChain.appendSequencerBatch(batch, 2000)
})
it('should return the number of inserted batch elements', async () => {
expect(await OVM_CanonicalTransactionChain.getTotalElements()).to.equal(
64
)
})
})
})
describe('getTotalBatches()', () => {
describe('when no batches have been inserted', () => {
it('should return zero', async () => {
expect(await OVM_CanonicalTransactionChain.getTotalBatches()).to.equal(
0
)
})
})
describe('when one batch has been inserted', () => {
beforeEach(async () => {
Mock__OVM_L1ToL2TransactionQueue.smocked.size.will.return.with(0)
await OVM_CanonicalTransactionChain.appendSequencerBatch(
[NON_NULL_BYTES32],
1000
)
})
it('should return the number of inserted batch elements', async () => {
expect(await OVM_CanonicalTransactionChain.getTotalBatches()).to.equal(
1
)
})
})
describe('when 8 batches have been inserted', () => {
beforeEach(async () => {
Mock__OVM_L1ToL2TransactionQueue.smocked.size.will.return.with(0)
for (let i = 0; i < 8; i++) {
await OVM_CanonicalTransactionChain.appendSequencerBatch(
[NON_NULL_BYTES32],
1000 * (i + 1)
)
}
})
it('should return the number of inserted batch elements', async () => {
expect(await OVM_CanonicalTransactionChain.getTotalBatches()).to.equal(
8
)
})
})
})
describe('verifyElement()', () => {
it('should revert when given an invalid batch header', async () => {
// TODO
})
it('should revert when given an invalid inclusion proof', async () => {
// TODO
})
it('should return true when given a valid proof', async () => {
// TODO
})
})
})
import { expect } from '../../../setup'
/* External Imports */
import { ethers } from '@nomiclabs/buidler'
import { Contract, ContractFactory, Signer, BigNumber } from 'ethers'
import { keccak256 } from 'ethers/lib/utils'
import _ from 'lodash'
/* Internal Imports */
import {
ZERO_ADDRESS,
NON_ZERO_ADDRESS,
DUMMY_BYTES32,
remove0x,
getBlockTime,
makeAddressManager,
} from '../../../helpers'
const calcBatchRoot = (element: any): string => {
return keccak256(
element.target +
remove0x(BigNumber.from(element.gasLimit).toHexString()).padStart(
64,
'0'
) +
remove0x(element.data)
)
}
const makeQueueElements = (count: number): any => {
return [...Array(count)].map((el, idx) => {
return {
target: NON_ZERO_ADDRESS,
gasLimit: idx + 30_000,
data: DUMMY_BYTES32[0],
}
})
}
describe.only('OVM_L1ToL2TransactionQueue', () => {
let signer: Signer
before(async () => {
;[signer] = await ethers.getSigners()
})
let AddressManager: Contract
before(async () => {
AddressManager = await makeAddressManager()
})
let Factory__OVM_L1ToL2TransactionQueue: ContractFactory
before(async () => {
Factory__OVM_L1ToL2TransactionQueue = await ethers.getContractFactory(
'OVM_L1ToL2TransactionQueue'
)
})
let OVM_L1ToL2TransactionQueue: Contract
beforeEach(async () => {
OVM_L1ToL2TransactionQueue = await Factory__OVM_L1ToL2TransactionQueue.deploy(
AddressManager.address
)
})
describe('enqueue()', () => {
it('should allow users to enqueue an element', async () => {
const [element] = makeQueueElements(1)
await expect(
OVM_L1ToL2TransactionQueue.enqueue(
element.target,
element.gasLimit,
element.data
)
).to.not.be.reverted
})
it('should allow users to enqueue more than one element', async () => {
for (const element of makeQueueElements(10)) {
await expect(
OVM_L1ToL2TransactionQueue.enqueue(
element.target,
element.gasLimit,
element.data
)
).to.not.be.reverted
}
})
})
describe('dequeue()', () => {
describe('when the sender is not the OVM_CanonicalTransactionChain', () => {
before(async () => {
await AddressManager.setAddress(
'OVM_CanonicalTransactionChain',
ZERO_ADDRESS
)
})
it('should revert', async () => {
await expect(OVM_L1ToL2TransactionQueue.dequeue()).to.be.revertedWith(
'Sender is not allowed to dequeue.'
)
})
})
describe('when the sender is the OVM_CanonicalTransactionChain', () => {
before(async () => {
await AddressManager.setAddress(
'OVM_CanonicalTransactionChain',
await signer.getAddress()
)
})
it('should revert if the queue is empty', async () => {
await expect(OVM_L1ToL2TransactionQueue.dequeue()).to.be.revertedWith(
'Queue is empty.'
)
})
it('should allow users to dequeue an element', async () => {
const [element] = makeQueueElements(1)
await OVM_L1ToL2TransactionQueue.enqueue(
element.target,
element.gasLimit,
element.data
)
await expect(OVM_L1ToL2TransactionQueue.dequeue()).to.not.be.reverted
})
it('should allow users to dequeue more than one element', async () => {
const elements = makeQueueElements(10)
for (const element of elements) {
await OVM_L1ToL2TransactionQueue.enqueue(
element.target,
element.gasLimit,
element.data
)
}
for (const element of elements) {
await expect(OVM_L1ToL2TransactionQueue.dequeue()).to.not.be.reverted
}
})
})
})
describe('size()', () => {
before(async () => {
await AddressManager.setAddress(
'OVM_CanonicalTransactionChain',
await signer.getAddress()
)
})
it('should return zero when no elements are in the queue', async () => {
const size = await OVM_L1ToL2TransactionQueue.size()
expect(size).to.equal(0)
})
it('should increase when new elements are enqueued', async () => {
const elements = makeQueueElements(10)
for (let i = 0; i < elements.length; i++) {
const element = elements[i]
await OVM_L1ToL2TransactionQueue.enqueue(
element.target,
element.gasLimit,
element.data
)
const size = await OVM_L1ToL2TransactionQueue.size()
expect(size).to.equal(i + 1)
}
})
it('should decrease when elements are dequeued', async () => {
const elements = makeQueueElements(10)
for (const element of elements) {
await OVM_L1ToL2TransactionQueue.enqueue(
element.target,
element.gasLimit,
element.data
)
}
for (let i = 0; i < elements.length; i++) {
await OVM_L1ToL2TransactionQueue.dequeue()
const size = await OVM_L1ToL2TransactionQueue.size()
expect(size).to.equal(elements.length - i - 1)
}
})
})
describe('peek()', () => {
before(async () => {
await AddressManager.setAddress(
'OVM_CanonicalTransactionChain',
await signer.getAddress()
)
})
it('should revert when the queue is empty', async () => {
await expect(OVM_L1ToL2TransactionQueue.peek()).to.be.revertedWith(
'Queue is empty.'
)
})
it('should return the front element if only one exists', async () => {
const [element] = makeQueueElements(1)
const result = await OVM_L1ToL2TransactionQueue.enqueue(
element.target,
element.gasLimit,
element.data
)
const timestamp = BigNumber.from(
await getBlockTime(ethers.provider, result.blockNumber)
)
expect(
_.toPlainObject(await OVM_L1ToL2TransactionQueue.peek())
).to.deep.include({
timestamp,
batchRoot: calcBatchRoot(element),
isL1ToL2Batch: true,
})
})
it('should return the front if more than one exists', async () => {
const elements = makeQueueElements(10)
const result = await OVM_L1ToL2TransactionQueue.enqueue(
elements[0].target,
elements[0].gasLimit,
elements[0].data
)
const timestamp = BigNumber.from(
await getBlockTime(ethers.provider, result.blockNumber)
)
for (const element of elements.slice(1)) {
expect(
_.toPlainObject(await OVM_L1ToL2TransactionQueue.peek())
).to.deep.include({
timestamp,
batchRoot: calcBatchRoot(elements[0]),
isL1ToL2Batch: true,
})
}
})
it('should return the new front when elements are dequeued', async () => {
const elements = makeQueueElements(10)
const timestamps: BigNumber[] = []
for (const element of elements) {
const result = await OVM_L1ToL2TransactionQueue.enqueue(
element.target,
element.gasLimit,
element.data
)
timestamps.push(
BigNumber.from(
await getBlockTime(ethers.provider, result.blockNumber)
)
)
}
for (let i = 0; i < elements.length - 1; i++) {
expect(
_.toPlainObject(await OVM_L1ToL2TransactionQueue.peek())
).to.deep.include({
timestamp: timestamps[i],
batchRoot: calcBatchRoot(elements[i]),
isL1ToL2Batch: true,
})
await OVM_L1ToL2TransactionQueue.dequeue()
}
})
})
})
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment