Commit 85da4979 authored by smartcontracts's avatar smartcontracts Committed by Kelvin Fichter

feat[contracts]: Replace Lib_RingBuffer with a much simpler Lib_Buffer (#821)

* feat[contracts]: replace Lib_RingBuffer with a simpler Lib_Buffer

* chore: changeset

* test: add tests for Lib_Buffer

* lint: fix

* test: add extra coverage for Lib_Buffer

* Update packages/contracts/contracts/optimistic-ethereum/libraries/utils/Lib_Buffer.sol
Co-authored-by: default avatarben-chain <ben@pseudonym.party>

* add some extra comments
Co-authored-by: default avatarben-chain <ben@pseudonym.party>
parent e8a95dff
---
'@eth-optimism/contracts': patch
---
Replaces RingBuffer with a simpler Buffer library
......@@ -2,7 +2,7 @@
pragma solidity >0.5.0 <0.8.0;
/* Library Imports */
import { Lib_RingBuffer } from "../../libraries/utils/Lib_RingBuffer.sol";
import { Lib_Buffer } from "../../libraries/utils/Lib_Buffer.sol";
import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
/* Interface Imports */
......@@ -28,7 +28,7 @@ contract OVM_ChainStorageContainer is iOVM_ChainStorageContainer, Lib_AddressRes
* Libraries *
*************/
using Lib_RingBuffer for Lib_RingBuffer.RingBuffer;
using Lib_Buffer for Lib_Buffer.Buffer;
/*************
......@@ -36,7 +36,7 @@ contract OVM_ChainStorageContainer is iOVM_ChainStorageContainer, Lib_AddressRes
*************/
string public owner;
Lib_RingBuffer.RingBuffer internal buffer;
Lib_Buffer.Buffer internal buffer;
/***************
......@@ -189,17 +189,4 @@ contract OVM_ChainStorageContainer is iOVM_ChainStorageContainer, Lib_AddressRes
_globalMetadata
);
}
/**
* @inheritdoc iOVM_ChainStorageContainer
*/
function setNextOverwritableIndex(
uint256 _index
)
override
public
onlyOwner
{
buffer.nextOverwritableIndex = _index;
}
}
......@@ -99,13 +99,4 @@ interface iOVM_ChainStorageContainer {
bytes27 _globalMetadata
)
external;
/**
* Marks an index as overwritable, meaing the underlying buffer can start to write values over
* any objects before and including the given index.
*/
function setNextOverwritableIndex(
uint256 _index
)
external;
}
......@@ -3,15 +3,15 @@ pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;
/* Library Imports */
import { Lib_RingBuffer } from "../../optimistic-ethereum/libraries/utils/Lib_RingBuffer.sol";
import { Lib_Buffer } from "../../optimistic-ethereum/libraries/utils/Lib_Buffer.sol";
/**
* @title TestLib_RingBuffer
* @title TestLib_Buffer
*/
contract TestLib_RingBuffer {
using Lib_RingBuffer for Lib_RingBuffer.RingBuffer;
contract TestLib_Buffer {
using Lib_Buffer for Lib_Buffer.Buffer;
Lib_RingBuffer.RingBuffer internal buf;
Lib_Buffer.Buffer internal buf;
function push(
bytes32 _value,
......@@ -37,11 +37,21 @@ contract TestLib_RingBuffer {
return buf.get(_index);
}
function deleteElementsAfterInclusive(
uint40 _index
)
public
{
return buf.deleteElementsAfterInclusive(
_index
);
}
function deleteElementsAfterInclusive(
uint40 _index,
bytes27 _extraData
)
internal
public
{
return buf.deleteElementsAfterInclusive(
_index,
......@@ -50,7 +60,7 @@ contract TestLib_RingBuffer {
}
function getLength()
internal
public
view
returns (
uint40
......@@ -59,8 +69,18 @@ contract TestLib_RingBuffer {
return buf.getLength();
}
function setExtraData(
bytes27 _extraData
)
public
{
return buf.setExtraData(
_extraData
);
}
function getExtraData()
internal
public
view
returns (
bytes27
......
import { expect } from '../../../setup'
import hre from 'hardhat'
import { Contract, ethers } from 'ethers'
describe('Lib_Buffer', () => {
let Lib_Buffer: Contract
beforeEach(async () => {
const Factory__Lib_Buffer = await hre.ethers.getContractFactory(
'TestLib_Buffer'
)
Lib_Buffer = await Factory__Lib_Buffer.deploy()
})
describe('push', () => {
for (const len of [1, 2, 4, 8, 32]) {
it(`it should be able to add ${len} element(s) to the array`, async () => {
for (let i = 0; i < len; i++) {
await expect(
Lib_Buffer.push(
ethers.utils.keccak256(`0x${i.toString(16).padStart(16, '0')}`),
`0x${'00'.repeat(27)}`
)
).to.not.be.reverted
}
})
}
})
describe('get', () => {
for (const len of [1, 2, 4, 8, 32]) {
describe(`when the array has ${len} element(s)`, () => {
const values = []
beforeEach(async () => {
for (let i = 0; i < len; i++) {
const value = ethers.utils.keccak256(
`0x${i.toString(16).padStart(16, '0')}`
)
values.push(value)
await Lib_Buffer.push(value, `0x${'00'.repeat(27)}`)
}
})
for (let i = 0; i < len; i += Math.max(1, len / 4)) {
it(`should be able to get the ${i}th/st/rd/whatever value`, async () => {
expect(await Lib_Buffer.get(i)).to.equal(values[i])
})
}
it('should throw if attempting to access an element that does not exist', async () => {
await expect(Lib_Buffer.get(len + 1)).to.be.reverted
})
})
}
})
describe('getLength', () => {
it('should return zero by default', async () => {
expect(await Lib_Buffer.getLength()).to.equal(0)
})
for (const len of [1, 2, 4, 8, 32]) {
describe(`when the array has ${len} element(s)`, () => {
const values = []
beforeEach(async () => {
for (let i = 0; i < len; i++) {
const value = ethers.utils.keccak256(
`0x${i.toString(16).padStart(16, '0')}`
)
values.push(value)
await Lib_Buffer.push(value, `0x${'00'.repeat(27)}`)
}
})
it(`should return a value of ${len}`, async () => {
expect(await Lib_Buffer.getLength()).to.equal(len)
})
})
}
})
describe('getExtraData', () => {
it('should be bytes27(0) by default', async () => {
expect(await Lib_Buffer.getExtraData()).to.equal(`0x${'00'.repeat(27)}`)
})
it('should change if set by a call to push()', async () => {
const extraData = `0x${'11'.repeat(27)}`
await Lib_Buffer.push(ethers.utils.keccak256('0x00'), extraData)
expect(await Lib_Buffer.getExtraData()).to.equal(extraData)
})
it('should change if set multiple times', async () => {
await Lib_Buffer.push(
ethers.utils.keccak256('0x00'),
`0x${'11'.repeat(27)}`
)
const extraData = `0x${'22'.repeat(27)}`
await Lib_Buffer.push(ethers.utils.keccak256('0x00'), extraData)
expect(await Lib_Buffer.getExtraData()).to.equal(extraData)
})
})
describe('setExtraData', () => {
it('should modify the extra data', async () => {
const extraData = `0x${'11'.repeat(27)}`
await Lib_Buffer.setExtraData(extraData)
expect(await Lib_Buffer.getExtraData()).to.equal(extraData)
})
it('should be able to modify the extra data multiple times', async () => {
const extraData1 = `0x${'22'.repeat(27)}`
await Lib_Buffer.setExtraData(extraData1)
expect(await Lib_Buffer.getExtraData()).to.equal(extraData1)
const extraData2 = `0x${'11'.repeat(27)}`
await Lib_Buffer.setExtraData(extraData2)
expect(await Lib_Buffer.getExtraData()).to.equal(extraData2)
})
})
describe('deleteElementsAfterInclusive', () => {
it('should revert when the array is empty', async () => {
await expect(Lib_Buffer['deleteElementsAfterInclusive(uint40)'](0)).to.be
.reverted
})
for (const len of [1, 2, 4, 8, 32]) {
describe(`when the array has ${len} element(s)`, () => {
const values = []
beforeEach(async () => {
for (let i = 0; i < len; i++) {
const value = ethers.utils.keccak256(
`0x${i.toString(16).padStart(16, '0')}`
)
values.push(value)
await Lib_Buffer.push(value, `0x${'00'.repeat(27)}`)
}
})
for (let i = len - 1; i > 0; i -= Math.max(1, len / 4)) {
it(`should be able to delete everything after and including the ${i}th/st/rd/whatever element`, async () => {
await expect(Lib_Buffer['deleteElementsAfterInclusive(uint40)'](i))
.to.not.be.reverted
expect(await Lib_Buffer.getLength()).to.equal(i)
await expect(Lib_Buffer.get(i)).to.be.reverted
})
}
for (let i = len - 1; i > 0; i -= Math.max(1, len / 4)) {
it(`should be able to delete after and incl. ${i}th/st/rd/whatever element while changing extra data`, async () => {
const extraData = `0x${i.toString(16).padStart(54, '0')}`
await expect(
Lib_Buffer['deleteElementsAfterInclusive(uint40,bytes27)'](
i,
extraData
)
).to.not.be.reverted
expect(await Lib_Buffer.getLength()).to.equal(i)
await expect(Lib_Buffer.get(i)).to.be.reverted
expect(await Lib_Buffer.getExtraData()).to.equal(extraData)
})
}
})
}
})
})
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