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 @@ ...@@ -2,7 +2,7 @@
pragma solidity >0.5.0 <0.8.0; pragma solidity >0.5.0 <0.8.0;
/* Library Imports */ /* 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"; import { Lib_AddressResolver } from "../../libraries/resolver/Lib_AddressResolver.sol";
/* Interface Imports */ /* Interface Imports */
...@@ -28,7 +28,7 @@ contract OVM_ChainStorageContainer is iOVM_ChainStorageContainer, Lib_AddressRes ...@@ -28,7 +28,7 @@ contract OVM_ChainStorageContainer is iOVM_ChainStorageContainer, Lib_AddressRes
* Libraries * * 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 ...@@ -36,7 +36,7 @@ contract OVM_ChainStorageContainer is iOVM_ChainStorageContainer, Lib_AddressRes
*************/ *************/
string public owner; 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 ...@@ -189,17 +189,4 @@ contract OVM_ChainStorageContainer is iOVM_ChainStorageContainer, Lib_AddressRes
_globalMetadata _globalMetadata
); );
} }
/**
* @inheritdoc iOVM_ChainStorageContainer
*/
function setNextOverwritableIndex(
uint256 _index
)
override
public
onlyOwner
{
buffer.nextOverwritableIndex = _index;
}
} }
...@@ -99,13 +99,4 @@ interface iOVM_ChainStorageContainer { ...@@ -99,13 +99,4 @@ interface iOVM_ChainStorageContainer {
bytes27 _globalMetadata bytes27 _globalMetadata
) )
external; 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; ...@@ -3,15 +3,15 @@ pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
/* Library Imports */ /* 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 { contract TestLib_Buffer {
using Lib_RingBuffer for Lib_RingBuffer.RingBuffer; using Lib_Buffer for Lib_Buffer.Buffer;
Lib_RingBuffer.RingBuffer internal buf; Lib_Buffer.Buffer internal buf;
function push( function push(
bytes32 _value, bytes32 _value,
...@@ -37,11 +37,21 @@ contract TestLib_RingBuffer { ...@@ -37,11 +37,21 @@ contract TestLib_RingBuffer {
return buf.get(_index); return buf.get(_index);
} }
function deleteElementsAfterInclusive(
uint40 _index
)
public
{
return buf.deleteElementsAfterInclusive(
_index
);
}
function deleteElementsAfterInclusive( function deleteElementsAfterInclusive(
uint40 _index, uint40 _index,
bytes27 _extraData bytes27 _extraData
) )
internal public
{ {
return buf.deleteElementsAfterInclusive( return buf.deleteElementsAfterInclusive(
_index, _index,
...@@ -50,7 +60,7 @@ contract TestLib_RingBuffer { ...@@ -50,7 +60,7 @@ contract TestLib_RingBuffer {
} }
function getLength() function getLength()
internal public
view view
returns ( returns (
uint40 uint40
...@@ -59,8 +69,18 @@ contract TestLib_RingBuffer { ...@@ -59,8 +69,18 @@ contract TestLib_RingBuffer {
return buf.getLength(); return buf.getLength();
} }
function setExtraData(
bytes27 _extraData
)
public
{
return buf.setExtraData(
_extraData
);
}
function getExtraData() function getExtraData()
internal public
view view
returns ( returns (
bytes27 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