Commit 8214c09c authored by Kelvin Fichter's avatar Kelvin Fichter

Started integrating existing unit tests

parent 455f595b
......@@ -26,7 +26,6 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
*******************************************/
iOVM_L1ToL2TransactionQueue internal ovmL1ToL2TransactionQueue;
address internal sequencer;
/*******************************************
......@@ -52,8 +51,6 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
Proxy_Resolver(_proxyManager)
{
ovmL1ToL2TransactionQueue = iOVM_L1ToL2TransactionQueue(resolve("OVM_L1ToL2TransactionQueue"));
sequencer = resolve("Sequencer");
forceInclusionPeriodSeconds = _forceInclusionPeriodSeconds;
}
......@@ -70,7 +67,7 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
public
{
require(
ovmL1ToL2TransactionQueue.size() > 0 == true,
ovmL1ToL2TransactionQueue.size() > 0,
"No batches are currently queued to be appended."
);
......@@ -98,12 +95,17 @@ contract OVM_CanonicalTransactionChain is iOVM_CanonicalTransactionChain, OVM_Ba
public
{
require(
msg.sender == sequencer,
"Function can only be called by the sequencer."
msg.sender == resolve("Sequencer"),
"Function can only be called by the Sequencer."
);
require(
_batch.length > 0,
"Cannot submit an empty batch."
);
require(
_timestamp >= lastOVMTimestamp,
_timestamp > lastOVMTimestamp,
"Batch timestamp must be later than the last OVM timestamp."
);
......
......@@ -90,7 +90,7 @@ contract OVM_StateCommitmentChain is iOVM_StateCommitmentChain, OVM_BaseChain, P
{
require(
msg.sender == address(ovmFraudVerifier),
"State batches can only be deleted by the fraud verifier"
"State batches can only be deleted by the OVM_FraudVerifier."
);
_deleteBatch(_batchHeader);
......
......@@ -10,7 +10,6 @@ import { Lib_OVMCodec } from "../../libraries/codec/Lib_OVMCodec.sol";
/* Interface Imports */
import { iOVM_L1ToL2TransactionQueue } from "../../iOVM/queue/iOVM_L1ToL2TransactionQueue.sol";
import { iOVM_CanonicalTransactionChain } from "../../iOVM/chain/iOVM_CanonicalTransactionChain.sol";
/* Contract Imports */
import { OVM_BaseQueue } from "./OVM_BaseQueue.sol";
......@@ -24,7 +23,7 @@ contract OVM_L1ToL2TransactionQueue is iOVM_L1ToL2TransactionQueue, OVM_BaseQueu
* Contract Variables: Contract References *
*******************************************/
iOVM_CanonicalTransactionChain internal ovmCanonicalTransactionChain;
address internal ovmCanonicalTransactionChain;
/***************
......@@ -39,7 +38,7 @@ contract OVM_L1ToL2TransactionQueue is iOVM_L1ToL2TransactionQueue, OVM_BaseQueu
)
Proxy_Resolver(_proxyManager)
{
ovmCanonicalTransactionChain = iOVM_CanonicalTransactionChain(resolve("OVM_CanonicalTransactionChain"));
ovmCanonicalTransactionChain = resolve("OVM_CanonicalTransactionChain");
}
......@@ -72,7 +71,7 @@ contract OVM_L1ToL2TransactionQueue is iOVM_L1ToL2TransactionQueue, OVM_BaseQueu
)
{
require(
msg.sender == address(ovmCanonicalTransactionChain),
msg.sender == ovmCanonicalTransactionChain,
"Sender is not allowed to enqueue."
);
......
......@@ -9,7 +9,7 @@ library Lib_MerkleUtils {
function getMerkleRoot(
bytes32[] memory _hashes
)
public
internal
view
returns (
bytes32 _root
......@@ -61,7 +61,7 @@ library Lib_MerkleUtils {
function getMerkleRoot(
bytes[] memory _elements
)
public
internal
view
returns (
bytes32 _root
......@@ -81,7 +81,7 @@ library Lib_MerkleUtils {
uint256 _path,
bytes32[] memory _siblings
)
public
internal
pure
returns (
bool _verified
......@@ -106,7 +106,7 @@ library Lib_MerkleUtils {
function _getDefaultHashes(
uint256 _length
)
internal
private
pure
returns (
bytes32[] memory _defaultHashes
......@@ -126,7 +126,7 @@ library Lib_MerkleUtils {
bytes32 _leftChildHash,
bytes32 _rightChildHash
)
internal
private
pure
returns (
bytes32 _hash
......
......@@ -7,7 +7,7 @@
"build": "yarn run build:contracts",
"build:contracts": "buidler compile",
"test": "yarn run test:contracts",
"test:contracts": "buidler test \"test/contracts/OVM/execution/OVM_test.spec.ts\""
"test:contracts": "buidler test \"test/contracts/OVM/chain/OVM_StateCommitmentChain.spec.ts\""
},
"devDependencies": {
"@nomiclabs/buidler": "^1.4.4",
......
import { expect } from '../../../setup'
/* External Imports */
import { ethers } from '@nomiclabs/buidler'
import { Signer, ContractFactory, Contract } from 'ethers'
/* Internal Imports */
import {
getProxyManager,
MockContract,
getMockContract,
setProxyTarget,
NON_NULL_BYTES32,
FORCE_INCLUSION_PERIOD_SECONDS,
ZERO_ADDRESS
} from '../../../helpers'
const getEthTime = async (): Promise<number> => {
return (await ethers.provider.getBlock('latest')).timestamp
}
const setEthTime = async (time: number): Promise<void> => {
await ethers.provider.send('evm_setNextBlockTimestamp', [time])
}
describe('OVM_CanonicalTransactionChain', () => {
let signer: Signer
before(async () => {
;[signer] = await ethers.getSigners()
})
let Proxy_Manager: Contract
before(async () => {
Proxy_Manager = await getProxyManager()
})
let Mock__OVM_L1ToL2TransactionQueue: MockContract
before(async () => {
Mock__OVM_L1ToL2TransactionQueue = await getMockContract(
await ethers.getContractFactory('OVM_L1ToL2TransactionQueue')
)
await setProxyTarget(
Proxy_Manager,
'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(
Proxy_Manager.address,
FORCE_INCLUSION_PERIOD_SECONDS
)
})
describe('appendQueueBatch()', () => {
describe('when the L1ToL2TransactionQueue queue is empty', () => {
before(() => {
Mock__OVM_L1ToL2TransactionQueue.setReturnValues('size', [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.setReturnValues('size', [1])
})
describe('when the inclusion delay period has not elapsed', () => {
beforeEach(async () => {
const timestamp = await getEthTime()
Mock__OVM_L1ToL2TransactionQueue.setReturnValues('peek', [
{
timestamp: timestamp,
batchRoot: NON_NULL_BYTES32,
isL1ToL2Batch: true
}
])
await setEthTime(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()
Mock__OVM_L1ToL2TransactionQueue.setReturnValues('peek', [
{
timestamp: timestamp,
batchRoot: NON_NULL_BYTES32,
isL1ToL2Batch: true
}
])
Mock__OVM_L1ToL2TransactionQueue.setReturnValues('dequeue', [
{
timestamp: timestamp,
batchRoot: NON_NULL_BYTES32,
isL1ToL2Batch: true
}
])
await setEthTime(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.getCallCount('dequeue')
).to.equal(1)
})
})
})
})
describe('appendSequencerBatch()', () => {
describe('when the sender is not the sequencer', () => {
before(async () => {
await Proxy_Manager.setProxy(
'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 Proxy_Manager.setProxy(
'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.setReturnValues('size', [1])
})
describe('when the first element in the queue is older than the provided batch', () => {
before(() => {
Mock__OVM_L1ToL2TransactionQueue.setReturnValues('peek', [
{
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.setReturnValues('peek', [
{
timestamp: 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.setReturnValues('size', [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.setReturnValues('size', [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.setReturnValues('size', [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.setReturnValues('size', [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.setReturnValues('size', [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.setReturnValues('size', [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 { Signer, ContractFactory, Contract } from 'ethers'
/* Internal Imports */
import {
getProxyManager,
MockContract,
getMockContract,
setProxyTarget,
NON_NULL_BYTES32,
ZERO_ADDRESS
} from '../../../helpers'
describe('OVM_StateCommitmentChain', () => {
let signer: Signer
before(async () => {
;[signer] = await ethers.getSigners()
})
let Proxy_Manager: Contract
before(async () => {
Proxy_Manager = await getProxyManager()
})
let Mock__OVM_CanonicalTransactionChain: MockContract
before(async () => {
Mock__OVM_CanonicalTransactionChain = await getMockContract(
await ethers.getContractFactory('OVM_CanonicalTransactionChain')
)
await setProxyTarget(
Proxy_Manager,
'OVM_CanonicalTransactionChain',
Mock__OVM_CanonicalTransactionChain
)
})
let Factory__OVM_StateCommitmentChain: ContractFactory
before(async () => {
Factory__OVM_StateCommitmentChain = await ethers.getContractFactory(
'OVM_StateCommitmentChain'
)
})
let OVM_StateCommitmentChain: Contract
beforeEach(async () => {
OVM_StateCommitmentChain = await Factory__OVM_StateCommitmentChain.deploy(
Proxy_Manager.address
)
})
describe('appendStateBatch', () => {
describe('when the provided batch is empty', () => {
const batch = []
it('should revert', async () => {
await expect(
OVM_StateCommitmentChain.appendStateBatch(batch)
).to.be.revertedWith('Cannot submit an empty state batch.')
})
})
describe('when the provided batch is not empty', () => {
const batch = [NON_NULL_BYTES32]
describe('when submitting more elements than present in the OVM_CanonicalTransactionChain', () => {
before(() => {
Mock__OVM_CanonicalTransactionChain.setReturnValues('getTotalElements', [batch.length - 1])
})
it('should revert', async () => {
await expect(
OVM_StateCommitmentChain.appendStateBatch(batch)
).to.be.revertedWith('Number of state roots cannot exceed the number of canonical transactions.')
})
})
describe('when not submitting more elements than present in the OVM_CanonicalTransactionChain', () => {
before(() => {
Mock__OVM_CanonicalTransactionChain.setReturnValues('getTotalElements', [batch.length])
})
it('should append the state batch', async () => {
await expect(
OVM_StateCommitmentChain.appendStateBatch(batch)
).to.not.be.reverted
// TODO: Check for correct insertion.
})
})
})
})
describe('deleteStateBatch', () => {
// TODO: Calculate the right header.
const batch = [NON_NULL_BYTES32]
const batchHeader = {
batchIndex: 0,
batchRoot: NON_NULL_BYTES32,
batchSize: 0,
prevTotalElements: 0,
extraData: '0x'
}
beforeEach(async () => {
Mock__OVM_CanonicalTransactionChain.setReturnValues('getTotalElements', [batch.length])
await OVM_StateCommitmentChain.appendStateBatch(batch)
})
describe('when the sender is not the OVM_FraudVerifier', () => {
before(async () => {
await Proxy_Manager.setProxy(
'OVM_FraudVerifier',
ZERO_ADDRESS
)
})
it('should revert', async () => {
await expect(
OVM_StateCommitmentChain.deleteStateBatch(batchHeader)
).to.be.revertedWith('State batches can only be deleted by the OVM_FraudVerifier.')
})
})
describe('when the sender is the OVM_FraudVerifier', () => {
before(async () => {
await Proxy_Manager.setProxy(
'OVM_FraudVerifier',
await signer.getAddress()
)
})
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('Invalid batch index.')
})
})
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: '0x1234'
})
).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
// TODO: Check that it deleted the batches.
})
})
})
})
})
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]
Mock__OVM_CanonicalTransactionChain.setReturnValues('getTotalElements', [batch.length])
await OVM_StateCommitmentChain.appendStateBatch(batch)
})
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', () => {
beforeEach(async () => {
const batch = Array(64).fill(NON_NULL_BYTES32)
Mock__OVM_CanonicalTransactionChain.setReturnValues('getTotalElements', [batch.length])
await OVM_StateCommitmentChain.appendStateBatch(batch)
})
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', () => {
beforeEach(async () => {
const batch = Array(32).fill(NON_NULL_BYTES32)
Mock__OVM_CanonicalTransactionChain.setReturnValues('getTotalElements', [batch.length * 2])
await OVM_StateCommitmentChain.appendStateBatch(batch)
await OVM_StateCommitmentChain.appendStateBatch(batch)
})
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]
Mock__OVM_CanonicalTransactionChain.setReturnValues('getTotalElements', [batch.length])
await OVM_StateCommitmentChain.appendStateBatch(batch)
})
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', () => {
beforeEach(async () => {
const batch = [NON_NULL_BYTES32]
Mock__OVM_CanonicalTransactionChain.setReturnValues('getTotalElements', [batch.length * 8])
for (let i = 0; i < 8; i++) {
await OVM_StateCommitmentChain.appendStateBatch(batch)
}
})
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 () => {
// TODO
})
it('should revert when given an invalid inclusion proof', async () => {
// TODO
})
it('should return true when given a valid proof', async () => {
// TODO
})
})
})
\ No newline at end of file
import { expect } from '../../../../../setup'
/* External Imports */
import { ethers } from '@nomiclabs/buidler'
import { Contract, ContractFactory, Signer } from 'ethers'
/* Internal Imports */
import { getProxyManager, MockContract, getMockContract, DUMMY_ACCOUNTS, setProxyTarget, ZERO_ADDRESS, fromHexString, toHexString, makeHexString, NULL_BYTES32, DUMMY_BYTES32, encodeRevertData, REVERT_FLAGS, NON_ZERO_ADDRESS, GAS_LIMIT } from '../../../../../helpers'
describe('OVM_ExecutionManager:opcodes:calling', () => {
let signer: Signer
before(async () => {
;[signer] = await ethers.getSigners()
})
let Proxy_Manager: Contract
before(async () => {
Proxy_Manager = await getProxyManager()
})
let Mock__OVM_StateManager: MockContract
before(async () => {
Mock__OVM_StateManager = await getMockContract('OVM_StateManager')
Mock__OVM_StateManager.setReturnValues('getAccount', (address: string) => {
return [
{
...DUMMY_ACCOUNTS[0].data,
ethAddress: address
}
]
})
await setProxyTarget(
Proxy_Manager,
'OVM_StateManager',
Mock__OVM_StateManager
)
})
let Factory__OVM_ExecutionManager: ContractFactory
before(async () => {
Factory__OVM_ExecutionManager = await ethers.getContractFactory(
'OVM_ExecutionManager'
)
})
let OVM_ExecutionManager: Contract
beforeEach(async () => {
OVM_ExecutionManager = await Factory__OVM_ExecutionManager.deploy(
Proxy_Manager.address
)
})
let Helper_CallTarget: Contract
let Helper_RevertDataViewer: Contract
beforeEach(async () => {
const Factory__Helper_CallTarget = await ethers.getContractFactory(
'Helper_CallTarget'
)
const Factory__Helper_RevertDataViewer = await ethers.getContractFactory(
'Helper_RevertDataViewer'
)
Helper_CallTarget = await Factory__Helper_CallTarget.deploy()
Helper_RevertDataViewer = await Factory__Helper_RevertDataViewer.deploy(
Helper_CallTarget.address
)
})
describe('ovmCALL', () => {
describe('when the OVM_StateManager has the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('hasAccount', [true])
})
describe('when the OVM_StateManager has already loaded the account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetAccountLoaded', [true])
})
describe('when the call does not revert', () => {
it('should return the result provided by the target contract', async () => {
const returnData = makeHexString('1234', 32)
expect(
await OVM_ExecutionManager.callStatic.ovmCALL(
GAS_LIMIT,
Helper_CallTarget.address,
Helper_CallTarget.interface.encodeFunctionData(
'doReturn',
[returnData]
)
)
).to.deep.equal([
true,
returnData
])
})
it('should set the ovmADDRESS to the target address', async () => {
expect(
await OVM_ExecutionManager.callStatic.ovmCALL(
GAS_LIMIT,
Helper_CallTarget.address,
Helper_CallTarget.interface.encodeFunctionData(
'doReturnADDRESS',
)
)
).to.deep.equal([
true,
ethers.utils.defaultAbiCoder.encode(
['address'],
[Helper_CallTarget.address]
)
])
})
})
describe('when the call does revert', () => {
describe('with no data', () => {
it('should return false with no data', async () => {
expect(
await OVM_ExecutionManager.callStatic.ovmCALL(
GAS_LIMIT,
Helper_CallTarget.address,
Helper_CallTarget.interface.encodeFunctionData(
'doRevert',
['0x']
)
)
).to.deep.equal([
false,
'0x'
])
})
})
describe('with the INTENTIONAL_REVERT flag', () => {
it('should return false with the flag and user-provided data', async () => {
})
})
describe('with the EXCEEDS_NUISANCE_GAS flag', () => {
it('should return false with the flag', async () => {
})
})
describe('with the INVALID_STATE_ACCESS flag', () => {
it('should revert with the INVALID_STATE_ACCESS flag', () => {
})
})
describe('with the UNSAFE_BYTECODE flag', () => {
it('should return false with the flag and no data', async () => {
})
})
})
})
describe('when the OVM_StateManager has not already loaded the account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetAccountLoaded', [false])
})
describe('when the call parent does not contain enough nuisance gas', () => {
it('should revert with the EXCEEDS_NUISANCE_GAS flag', () => {
})
})
})
})
describe('when the OVM_StateManager does not have the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('hasAccount', [false])
})
it('should revert with the INVALID_STATE_ACCESS flag', () => {
})
})
})
describe('ovmSTATICCALL', () => {
describe('when the OVM_StateManager has the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('hasAccount', [true])
})
describe('when the OVM_StateManager has already loaded the account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetAccountLoaded', [true])
})
describe('when the call does not revert', () => {
it('should return the result provided by the target contract', async () => {
})
it('should set the context to static', async () => {
})
})
describe('when the call does revert', () => {
describe('with no data', () => {
it('should return false with no data', async () => {
})
})
describe('with the INTENTIONAL_REVERT flag', () => {
it('should return false with the flag and user-provided data', async () => {
})
})
describe('with the EXCEEDS_NUISANCE_GAS flag', () => {
it('should return false with the flag', async () => {
})
})
describe('with the INVALID_STATE_ACCESS flag', () => {
it('should revert with the INVALID_STATE_ACCESS flag', () => {
})
})
describe('with the UNSAFE_BYTECODE flag', () => {
it('should return false with the flag and no data', async () => {
})
})
})
})
describe('when the OVM_StateManager has not already loaded the account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetAccountLoaded', [false])
})
describe('when the call parent does not contain enough nuisance gas', () => {
it('should revert with the EXCEEDS_NUISANCE_GAS flag', () => {
})
})
})
})
describe('when the OVM_StateManager does not have the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('hasAccount', [false])
})
it('should revert with the INVALID_STATE_ACCESS flag', () => {
})
})
})
describe('ovmDELEGATECALL', () => {
describe('when the OVM_StateManager has the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('hasAccount', [true])
})
describe('when the OVM_StateManager has already loaded the account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetAccountLoaded', [true])
})
describe('when the call does not revert', () => {
it('should return the result provided by the target contract', async () => {
})
it('should retain the previous ovmADDRESS', async () => {
})
})
describe('when the call does revert', () => {
describe('with no data', () => {
it('should return false with no data', async () => {
})
})
describe('with the INTENTIONAL_REVERT flag', () => {
it('should return false with the flag and user-provided data', async () => {
})
})
describe('with the EXCEEDS_NUISANCE_GAS flag', () => {
it('should return false with the flag', async () => {
})
})
describe('with the INVALID_STATE_ACCESS flag', () => {
it('should revert with the INVALID_STATE_ACCESS flag', () => {
})
})
describe('with the UNSAFE_BYTECODE flag', () => {
it('should return false with the flag and no data', async () => {
})
})
})
})
describe('when the OVM_StateManager has not already loaded the account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetAccountLoaded', [false])
})
describe('when the call parent does not contain enough nuisance gas', () => {
it('should revert with the EXCEEDS_NUISANCE_GAS flag', () => {
})
})
})
})
describe('when the OVM_StateManager does not have the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('hasAccount', [false])
})
it('should revert with the INVALID_STATE_ACCESS flag', () => {
})
})
})
})
import { expect } from '../../../../../setup'
/* External Imports */
import { ethers } from '@nomiclabs/buidler'
import { Contract, ContractFactory } from 'ethers'
/* Internal Imports */
import { getProxyManager, MockContract, getMockContract, DUMMY_ACCOUNTS, setProxyTarget, ZERO_ADDRESS, fromHexString, toHexString, makeHexString, NULL_BYTES32, DUMMY_BYTES32, encodeRevertData, REVERT_FLAGS } from '../../../../../helpers'
describe('OVM_ExecutionManager:opcodes:code', () => {
let Proxy_Manager: Contract
before(async () => {
Proxy_Manager = await getProxyManager()
})
let Mock__OVM_StateManager: MockContract
before(async () => {
Mock__OVM_StateManager = await getMockContract('OVM_StateManager')
Mock__OVM_StateManager.setReturnValues('getAccount', (address: string) => {
return [
{
...DUMMY_ACCOUNTS[0].data,
ethAddress: address
}
]
})
await setProxyTarget(
Proxy_Manager,
'OVM_StateManager',
Mock__OVM_StateManager
)
})
let Dummy_Contract: Contract
before(async () => {
// We need some contract to query code for, might as well reuse an existing object.
Dummy_Contract = Mock__OVM_StateManager
})
let Factory__OVM_ExecutionManager: ContractFactory
before(async () => {
Factory__OVM_ExecutionManager = await ethers.getContractFactory(
'OVM_ExecutionManager'
)
})
let OVM_ExecutionManager: Contract
beforeEach(async () => {
OVM_ExecutionManager = await Factory__OVM_ExecutionManager.deploy(
Proxy_Manager.address
)
})
let Helper_RevertDataViewer: Contract
beforeEach(async () => {
const Factory__Helper_RevertDataViewer = await ethers.getContractFactory(
'Helper_RevertDataViewer'
)
Helper_RevertDataViewer = await Factory__Helper_RevertDataViewer.deploy(
OVM_ExecutionManager.address
)
})
describe('ovmEXTCODECOPY()', () => {
describe('when the OVM_StateManager has the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('hasAccount', [true])
})
describe('when the OVM_StateManager has already loaded the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetAccountLoaded', [true])
})
it('should return the code for a given account', async () => {
const expectedCode = await ethers.provider.getCode(Dummy_Contract.address)
const expectedCodeSize = fromHexString(expectedCode).length
expect(
await OVM_ExecutionManager.callStatic.ovmEXTCODECOPY(Dummy_Contract.address, 0, expectedCodeSize)
).to.equal(expectedCode)
})
it('should return empty if the provided length is zero', async () => {
const expectedCode = '0x'
expect(
await OVM_ExecutionManager.callStatic.ovmEXTCODECOPY(Dummy_Contract.address, 0, 0)
).to.equal(expectedCode)
})
it('should return offset code when offset is less than total length', async () => {
const fullCode = await ethers.provider.getCode(Dummy_Contract.address)
const fullCodeSize = fromHexString(fullCode).length
const codeOffset = Math.floor(fullCodeSize / 2)
const codeLength = fullCodeSize - codeOffset
const expectedCode = toHexString(fromHexString(fullCode).slice(codeOffset, codeOffset + codeLength))
expect(
await OVM_ExecutionManager.callStatic.ovmEXTCODECOPY(Dummy_Contract.address, codeOffset, codeLength)
).to.equal(expectedCode)
})
it('should return less code when length is less than total length', async () => {
const fullCode = await ethers.provider.getCode(Dummy_Contract.address)
const fullCodeSize = fromHexString(fullCode).length
const codeLength = Math.floor(fullCodeSize / 2)
const expectedCode = toHexString(fromHexString(fullCode).slice(0, codeLength))
expect(
await OVM_ExecutionManager.callStatic.ovmEXTCODECOPY(Dummy_Contract.address, 0, codeLength)
).to.equal(expectedCode)
})
it('should return extra code when length is greater than total length', async () => {
const fullCode = await ethers.provider.getCode(Dummy_Contract.address)
const fullCodeSize = fromHexString(fullCode).length
const extraLength = fullCodeSize
const codeLength = fullCodeSize + extraLength
const expectedCode = toHexString(Buffer.concat([
fromHexString(fullCode),
fromHexString(makeHexString('00', extraLength))
]))
expect(
await OVM_ExecutionManager.callStatic.ovmEXTCODECOPY(Dummy_Contract.address, 0, codeLength)
).to.equal(expectedCode)
})
it('should return extra code when offset is less than total length and length is greater than total length', async () => {
const fullCode = await ethers.provider.getCode(Dummy_Contract.address)
const fullCodeSize = fromHexString(fullCode).length
const extraLength = fullCodeSize
const codeOffset = Math.floor(fullCodeSize / 2)
const codeLength = fullCodeSize - codeOffset + extraLength
const expectedCode = toHexString(Buffer.concat([
fromHexString(fullCode).slice(codeOffset, codeOffset + codeLength),
fromHexString(makeHexString('00', extraLength))
]))
expect(
await OVM_ExecutionManager.callStatic.ovmEXTCODECOPY(Dummy_Contract.address, codeOffset, codeLength)
).to.equal(expectedCode)
})
it('should return empty bytes when both offset and length exceed total length', async () => {
const fullCode = await ethers.provider.getCode(Dummy_Contract.address)
const fullCodeSize = fromHexString(fullCode).length
const extraLength = fullCodeSize
const codeOffset = fullCodeSize
const codeLength = fullCodeSize + extraLength
const expectedCode = toHexString(Buffer.concat([
fromHexString(makeHexString('00', codeLength))
]))
expect(
await OVM_ExecutionManager.callStatic.ovmEXTCODECOPY(Dummy_Contract.address, codeOffset, codeLength)
).to.equal(expectedCode)
})
})
describe('when the OVM_StateManager has not already loaded the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetAccountLoaded', [false])
})
it('should revert with the EXCEEDS_NUISANCE_GAS flag', async () => {
const calldata = OVM_ExecutionManager.interface.encodeFunctionData(
'ovmEXTCODECOPY',
[
Dummy_Contract.address,
0,
0
]
)
await Helper_RevertDataViewer.fallback({
data: calldata
})
expect(
await Helper_RevertDataViewer.revertdata()
).to.equal(encodeRevertData(
REVERT_FLAGS.EXCEEDS_NUISANCE_GAS
))
})
})
})
describe('when the OVM_StateManager does not have the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('hasAccount', [false])
})
it('should revert with the INVALID_STATE_ACCESS flag', async () => {
const calldata = OVM_ExecutionManager.interface.encodeFunctionData(
'ovmEXTCODECOPY',
[
Dummy_Contract.address,
0,
0
]
)
await Helper_RevertDataViewer.fallback({
data: calldata
})
expect(
await Helper_RevertDataViewer.revertdata()
).to.equal(encodeRevertData(
REVERT_FLAGS.INVALID_STATE_ACCESS
))
})
})
})
describe('ovmEXTCODESIZE()', () => {
describe('when the OVM_StateManager has the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('hasAccount', [true])
})
describe('when the OVM_StateManager has already loaded the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetAccountLoaded', [true])
})
it('should return the code size for a given account', async () => {
const expectedCode = await ethers.provider.getCode(Dummy_Contract.address)
const expectedCodeSize = fromHexString(expectedCode).length
expect(
await OVM_ExecutionManager.callStatic.ovmEXTCODESIZE(Dummy_Contract.address)
).to.equal(expectedCodeSize)
})
it('should return zero if the account has no code', async () => {
const expectedCodeSize = 0
expect(
await OVM_ExecutionManager.callStatic.ovmEXTCODESIZE(ZERO_ADDRESS)
).to.equal(expectedCodeSize)
})
})
describe('when the OVM_StateManager has not already loaded the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetAccountLoaded', [false])
})
it('should revert with the EXCEEDS_NUISANCE_GAS flag', async () => {
const calldata = OVM_ExecutionManager.interface.encodeFunctionData(
'ovmEXTCODESIZE',
[
Dummy_Contract.address,
]
)
await Helper_RevertDataViewer.fallback({
data: calldata
})
expect(
await Helper_RevertDataViewer.revertdata()
).to.equal(encodeRevertData(
REVERT_FLAGS.EXCEEDS_NUISANCE_GAS
))
})
})
})
describe('when the OVM_StateManager does not have the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('hasAccount', [false])
})
it('should revert with the INVALID_STATE_ACCESS flag', async () => {
const calldata = OVM_ExecutionManager.interface.encodeFunctionData(
'ovmEXTCODESIZE',
[
Dummy_Contract.address
]
)
await Helper_RevertDataViewer.fallback({
data: calldata
})
expect(
await Helper_RevertDataViewer.revertdata()
).to.equal(encodeRevertData(
REVERT_FLAGS.INVALID_STATE_ACCESS
))
})
})
})
describe('ovmEXTCODEHASH()', () => {
describe('when the OVM_StateManager has the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('hasAccount', [true])
})
describe('when the OVM_StateManager has already loaded the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetAccountLoaded', [true])
})
it('should return the code hash for a given account', async () => {
const expectedCode = await ethers.provider.getCode(Dummy_Contract.address)
const expectedCodeHash = ethers.utils.keccak256(expectedCode)
expect(
await OVM_ExecutionManager.callStatic.ovmEXTCODEHASH(Dummy_Contract.address)
).to.equal(expectedCodeHash)
})
it('should return zero if the account does not exist', async () => {
const expectedCodeHash = NULL_BYTES32
expect(
await OVM_ExecutionManager.callStatic.ovmEXTCODEHASH(ZERO_ADDRESS)
).to.equal(expectedCodeHash)
})
})
describe('when the OVM_StateManager has not already loaded the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetAccountLoaded', [false])
})
it('should revert with the EXCEEDS_NUISANCE_GAS flag', async () => {
const calldata = OVM_ExecutionManager.interface.encodeFunctionData(
'ovmEXTCODEHASH',
[
Dummy_Contract.address,
]
)
await Helper_RevertDataViewer.fallback({
data: calldata
})
expect(
await Helper_RevertDataViewer.revertdata()
).to.equal(encodeRevertData(
REVERT_FLAGS.EXCEEDS_NUISANCE_GAS
))
})
})
})
describe('when the OVM_StateManager does not have the corresponding account', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('hasAccount', [false])
})
it('should revert with the INVALID_STATE_ACCESS flag', async () => {
const calldata = OVM_ExecutionManager.interface.encodeFunctionData(
'ovmEXTCODEHASH',
[
Dummy_Contract.address
]
)
await Helper_RevertDataViewer.fallback({
data: calldata
})
expect(
await Helper_RevertDataViewer.revertdata()
).to.equal(encodeRevertData(
REVERT_FLAGS.INVALID_STATE_ACCESS
))
})
})
})
})
import { expect } from '../../../../../setup'
/* External Imports */
import { ethers } from '@nomiclabs/buidler'
import { Contract, ContractFactory } from 'ethers'
describe('OVM_ExecutionManager:opcodes:creation', () => {
describe('ovmCREATE', () => {
})
describe('ovmCREATE2', () => {
})
})
import { expect } from '../../../../../setup'
/* External Imports */
import { ethers } from '@nomiclabs/buidler'
import { Contract, ContractFactory } from 'ethers'
/* Internal Imports */
import { getProxyManager, encodeRevertData, REVERT_FLAGS } from '../../../../../helpers'
describe('OVM_ExecutionManager:opcodes:halting', () => {
let Proxy_Manager: Contract
before(async () => {
Proxy_Manager = await getProxyManager()
})
let Factory__OVM_ExecutionManager: ContractFactory
before(async () => {
Factory__OVM_ExecutionManager = await ethers.getContractFactory(
'OVM_ExecutionManager'
)
})
let OVM_ExecutionManager: Contract
beforeEach(async () => {
OVM_ExecutionManager = await Factory__OVM_ExecutionManager.deploy(
Proxy_Manager.address
)
})
let Helper_RevertDataViewer: Contract
beforeEach(async () => {
const Factory__Helper_RevertDataViewer = await ethers.getContractFactory(
'Helper_RevertDataViewer'
)
Helper_RevertDataViewer = await Factory__Helper_RevertDataViewer.deploy(OVM_ExecutionManager.address)
})
describe('ovmREVERT', () => {
it('should revert with the provided data prefixed by the intentional revert flag', async () => {
const revertdata = '12345678'.repeat(10)
const calldata = OVM_ExecutionManager.interface.encodeFunctionData(
'ovmREVERT',
['0x' + revertdata]
)
await Helper_RevertDataViewer.fallback({
data: calldata
})
expect(
await Helper_RevertDataViewer.revertdata()
).to.equal(encodeRevertData(
REVERT_FLAGS.INTENTIONAL_REVERT,
'0x' + revertdata
))
})
it('should revert with the intentional revert flag if no data is provided', async () => {
const calldata = OVM_ExecutionManager.interface.encodeFunctionData(
'ovmREVERT',
['0x']
)
await Helper_RevertDataViewer.fallback({
data: calldata
})
expect(
await Helper_RevertDataViewer.revertdata()
).to.equal(encodeRevertData(
REVERT_FLAGS.INTENTIONAL_REVERT
))
})
})
})
import { expect } from '../../../../../setup'
/* External Imports */
import { ethers } from '@nomiclabs/buidler'
import { Contract, ContractFactory } from 'ethers'
/* Internal Imports */
import { getProxyManager, MockContract, getMockContract, DUMMY_ACCOUNTS, setProxyTarget, ZERO_ADDRESS, fromHexString, toHexString, makeHexString, NULL_BYTES32, DUMMY_BYTES32, encodeRevertData, REVERT_FLAGS, NON_ZERO_ADDRESS } from '../../../../../helpers'
describe('OVM_ExecutionManager:opcodes:storage', () => {
let Proxy_Manager: Contract
before(async () => {
Proxy_Manager = await getProxyManager()
})
let Mock__OVM_StateManager: MockContract
before(async () => {
Mock__OVM_StateManager = await getMockContract('OVM_StateManager')
await setProxyTarget(
Proxy_Manager,
'OVM_StateManager',
Mock__OVM_StateManager
)
})
let Factory__OVM_ExecutionManager: ContractFactory
before(async () => {
Factory__OVM_ExecutionManager = await ethers.getContractFactory(
'OVM_ExecutionManager'
)
})
let OVM_ExecutionManager: Contract
beforeEach(async () => {
OVM_ExecutionManager = await Factory__OVM_ExecutionManager.deploy(
Proxy_Manager.address
)
})
let Helper_RevertDataViewer: Contract
beforeEach(async () => {
const Factory__Helper_RevertDataViewer = await ethers.getContractFactory(
'Helper_RevertDataViewer'
)
Helper_RevertDataViewer = await Factory__Helper_RevertDataViewer.deploy(
OVM_ExecutionManager.address
)
})
const DUMMY_SLOT_KEY = DUMMY_BYTES32[0]
const DUMMY_SLOT_VALUE = DUMMY_BYTES32[1]
describe('ovmSLOAD', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('getContractStorage', () => {
return [
DUMMY_SLOT_VALUE
]
})
})
describe('when the OVM_StateManager has the corresponding storage slot', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('hasContractStorage', [true])
})
describe('when the OVM_StateManager has already loaded the storage slot', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetContractStorageLoaded', [true])
})
it('should return the value of the storage slot', async () => {
expect(
await OVM_ExecutionManager.callStatic.ovmSLOAD(
DUMMY_SLOT_KEY
)
).to.equal(DUMMY_SLOT_VALUE)
})
})
describe('when the OVM_StateManager has not already loaded the storage slot', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetContractStorageLoaded', [false])
})
it('should revert with the EXCEEDS_NUISANCE_GAS flag', async () => {
const calldata = OVM_ExecutionManager.interface.encodeFunctionData(
'ovmSLOAD',
[
DUMMY_SLOT_KEY
]
)
await Helper_RevertDataViewer.fallback({
data: calldata
})
expect(
await Helper_RevertDataViewer.revertdata()
).to.equal(encodeRevertData(
REVERT_FLAGS.EXCEEDS_NUISANCE_GAS
))
})
})
})
describe('when the OVM_StateManager does not have the corresponding storage slot', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('hasContractStorage', [false])
})
it('should revert with the INVALID_STATE_ACCESS flag', async () => {
const calldata = OVM_ExecutionManager.interface.encodeFunctionData(
'ovmSLOAD',
[
DUMMY_SLOT_KEY
]
)
await Helper_RevertDataViewer.fallback({
data: calldata
})
expect(
await Helper_RevertDataViewer.revertdata()
).to.equal(encodeRevertData(
REVERT_FLAGS.INVALID_STATE_ACCESS
))
})
})
})
describe('ovmSSTORE', () => {
describe('when the OVM_StateManager has already changed the storage slot', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetContractStorageChanged', [true])
})
it('should modify the storage slot value', async () => {
await expect(
OVM_ExecutionManager.ovmSSTORE(
DUMMY_SLOT_KEY,
DUMMY_SLOT_VALUE
)
).to.not.be.reverted
expect(
Mock__OVM_StateManager.getCallData('putContractStorage', 0)
).to.deep.equal(
[
ZERO_ADDRESS,
DUMMY_SLOT_KEY,
DUMMY_SLOT_VALUE
]
)
})
})
describe('when the OVM_StateManager has not already changed the storage slot', () => {
before(() => {
Mock__OVM_StateManager.setReturnValues('testAndSetContractStorageChanged', [false])
})
it('should revert with the EXCEEDS_NUISANCE_GAS flag', async () => {
const calldata = OVM_ExecutionManager.interface.encodeFunctionData(
'ovmSSTORE',
[
DUMMY_SLOT_KEY,
DUMMY_SLOT_VALUE
]
)
await Helper_RevertDataViewer.fallback({
data: calldata
})
expect(
await Helper_RevertDataViewer.revertdata()
).to.equal(encodeRevertData(
REVERT_FLAGS.EXCEEDS_NUISANCE_GAS
))
})
})
})
})
/* Internal Imports */
import { runExecutionManagerTest } from '../../../helpers/test-utils/test-parsing'
import { NON_NULL_BYTES32, GAS_LIMIT } from '../../../helpers'
runExecutionManagerTest(
{
name: 'Top level test',
preState: {
ExecutionManager: {
ovmStateManager: "$OVM_STATE_MANAGER",
messageRecord: {
nuisanceGasLeft: GAS_LIMIT / 2
}
},
StateManager: {
accounts: {
"$DUMMY_OVM_ADDRESS_1": {
codeHash: NON_NULL_BYTES32,
ethAddress: "$OVM_CALL_HELPER"
},
"$DUMMY_OVM_ADDRESS_2": {
codeHash: NON_NULL_BYTES32,
ethAddress: "$OVM_CALL_HELPER"
}
}
}
},
parameters: [
{
name: 'Do two ovmCALLs to one address',
postState: {
ExecutionManager: {
messageRecord: {
nuisanceGasLeft: GAS_LIMIT / 2 - (332) * 100 * 1
}
}
},
parameters: [
{
steps: [
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT / 2,
"$DUMMY_OVM_ADDRESS_1",
[
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT / 2,
"$DUMMY_OVM_ADDRESS_1",
[
{
functionName: 'ovmADDRESS',
functionParams: [],
returnStatus: true,
returnValues: ["$DUMMY_OVM_ADDRESS_1"]
},
{
functionName: 'ovmCALLER',
functionParams: [],
returnStatus: true,
returnValues: ["$DUMMY_OVM_ADDRESS_1"]
}
]
],
returnStatus: true,
returnValues: []
},
]
],
returnStatus: true,
returnValues: []
}
]
}
]
},
{
name: 'Do two ovmCALLs to two different addresses',
postState: {
ExecutionManager: {
messageRecord: {
nuisanceGasLeft: GAS_LIMIT / 2 - (332) * 100 * 2
}
}
},
parameters: [
{
steps: [
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT / 2,
"$DUMMY_OVM_ADDRESS_1",
[
{
functionName: 'ovmCALL',
functionParams: [
GAS_LIMIT / 2,
"$DUMMY_OVM_ADDRESS_2",
[
{
functionName: 'ovmADDRESS',
functionParams: [],
returnStatus: true,
returnValues: ["$DUMMY_OVM_ADDRESS_2"]
},
{
functionName: 'ovmCALLER',
functionParams: [],
returnStatus: true,
returnValues: ["$DUMMY_OVM_ADDRESS_1"]
}
]
],
returnStatus: true,
returnValues: []
},
]
],
returnStatus: true,
returnValues: []
}
]
}
]
}
]
}
)
import { expect } from '../../../setup'
/* External Imports */
import { ethers } from '@nomiclabs/buidler'
import { Contract, ContractFactory, Signer } from 'ethers'
/* Internal Imports */
import {
getProxyManager, ZERO_ADDRESS, NULL_BYTES32
} from '../../../helpers'
const parseQueueElement = (result: any[]): any => {
return {
timestamp: result[0].toNumber(),
batchRoot: result[1],
isL1ToL2Batch: result[2],
}
}
const makeQueueElements = (count: number): any => {
const elements = []
for (let i = 0; i < count; i++) {
elements.push({
timestamp: Date.now(),
batchRoot: NULL_BYTES32,
isL1ToL2Batch: false,
})
}
return elements
}
describe('OVM_L1ToL2TransactionQueue', () => {
let signer: Signer
before(async () => {
;[signer] = await ethers.getSigners()
})
let Proxy_Manager: Contract
before(async () => {
Proxy_Manager = await getProxyManager()
})
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(
Proxy_Manager.address
)
})
describe('enqueue()', () => {
it('should allow users to enqueue an element', async () => {
const [element] = makeQueueElements(1)
await expect(OVM_L1ToL2TransactionQueue.enqueue(element)).to.not.be.reverted
})
it('should allow users to enqueue more than one element', async () => {
const elements = makeQueueElements(10)
for (let i = 0; i < elements.length; i++) {
await expect(OVM_L1ToL2TransactionQueue.enqueue(elements[i])).to.not.be.reverted
}
})
})
describe('dequeue()', () => {
describe('when the sender is not the OVM_CanonicalTransactionChain', () => {
before(async () => {
await Proxy_Manager.setProxy(
'OVM_CanonicalTransactionChain',
ZERO_ADDRESS
)
})
it('should revert', async () => {
await expect(OVM_L1ToL2TransactionQueue.dequeue()).to.be.revertedWith('Sender is not allowed to enqueue.')
})
})
describe('when the sender is the OVM_CanonicalTransactionChain', () => {
before(async () => {
await Proxy_Manager.setProxy(
'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)
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 (let i = 0; i < elements.length; i++) {
await OVM_L1ToL2TransactionQueue.enqueue(elements[i])
}
for (let i = 0; i < elements.length; i++) {
await expect(OVM_L1ToL2TransactionQueue.dequeue()).to.not.be.reverted
}
})
})
})
describe('size()', () => {
before(async () => {
await Proxy_Manager.setProxy(
'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++) {
await OVM_L1ToL2TransactionQueue.enqueue(elements[i])
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 (let i = 0; i < elements.length; i++) {
await OVM_L1ToL2TransactionQueue.enqueue(elements[i])
}
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 Proxy_Manager.setProxy(
'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)
await OVM_L1ToL2TransactionQueue.enqueue(element)
const front = await OVM_L1ToL2TransactionQueue.peek()
expect(parseQueueElement(front)).to.deep.equal(element)
})
it('should return the front if more than one exists', async () => {
const elements = makeQueueElements(10)
for (let i = 0; i < elements.length; i++) {
await OVM_L1ToL2TransactionQueue.enqueue(elements[i])
const front = await OVM_L1ToL2TransactionQueue.peek()
expect(parseQueueElement(front)).to.deep.equal(elements[0])
}
})
it('should return the new front when elements are dequeued', async () => {
const elements = makeQueueElements(10)
for (let i = 0; i < elements.length; i++) {
await OVM_L1ToL2TransactionQueue.enqueue(elements[i])
}
for (let i = 0; i < elements.length - 1; i++) {
const front = await OVM_L1ToL2TransactionQueue.peek()
expect(parseQueueElement(front)).to.deep.equal(elements[i + 1])
await OVM_L1ToL2TransactionQueue.dequeue()
}
})
})
})
......@@ -19,3 +19,5 @@ export const NULL_BYTES32 = makeHexString('00', 32)
export const NON_NULL_BYTES32 = makeHexString('11', 32)
export const ZERO_ADDRESS = makeAddress('00')
export const NON_ZERO_ADDRESS = makeAddress('11')
export const FORCE_INCLUSION_PERIOD_SECONDS = 600
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