|
@@ -6,28 +6,48 @@ const { batchInBlock } = require('../helpers/txpool');
|
|
|
|
|
|
const $Checkpoints = artifacts.require('$Checkpoints');
|
|
const $Checkpoints = artifacts.require('$Checkpoints');
|
|
|
|
|
|
|
|
+// The library name may be 'Checkpoints' or 'CheckpointsUpgradeable'
|
|
|
|
+const libraryName = $Checkpoints._json.contractName.replace(/^\$/, '');
|
|
|
|
+
|
|
|
|
+const traceLengths = [160, 224];
|
|
|
|
+
|
|
const first = array => (array.length ? array[0] : undefined);
|
|
const first = array => (array.length ? array[0] : undefined);
|
|
const last = array => (array.length ? array[array.length - 1] : undefined);
|
|
const last = array => (array.length ? array[array.length - 1] : undefined);
|
|
|
|
|
|
contract('Checkpoints', function () {
|
|
contract('Checkpoints', function () {
|
|
beforeEach(async function () {
|
|
beforeEach(async function () {
|
|
this.mock = await $Checkpoints.new();
|
|
this.mock = await $Checkpoints.new();
|
|
|
|
+
|
|
|
|
+ this.methods = { trace: {} };
|
|
|
|
+ this.methods.history = {
|
|
|
|
+ latest: (...args) => this.mock.methods[`$latest_${libraryName}_History(uint256)`](0, ...args),
|
|
|
|
+ latestCheckpoint: (...args) => this.mock.methods[`$latestCheckpoint_${libraryName}_History(uint256)`](0, ...args),
|
|
|
|
+ length: (...args) => this.mock.methods[`$length_${libraryName}_History(uint256)`](0, ...args),
|
|
|
|
+ push: (...args) => this.mock.methods['$push(uint256,uint256)'](0, ...args),
|
|
|
|
+ getAtBlock: (...args) => this.mock.$getAtBlock(0, ...args),
|
|
|
|
+ getAtRecentBlock: (...args) => this.mock.$getAtProbablyRecentBlock(0, ...args),
|
|
|
|
+ };
|
|
|
|
+ for (const length of traceLengths) {
|
|
|
|
+ this.methods.trace[length] = {
|
|
|
|
+ latest: (...args) => this.mock.methods[`$latest_${libraryName}_Trace${length}(uint256)`](0, ...args),
|
|
|
|
+ latestCheckpoint: (...args) =>
|
|
|
|
+ this.mock.methods[`$latestCheckpoint_${libraryName}_Trace${length}(uint256)`](0, ...args),
|
|
|
|
+ length: (...args) => this.mock.methods[`$length_${libraryName}_Trace${length}(uint256)`](0, ...args),
|
|
|
|
+ push: (...args) => this.mock.methods[`$push(uint256,uint${256 - length},uint${length})`](0, ...args),
|
|
|
|
+ lowerLookup: (...args) => this.mock.methods[`$lowerLookup(uint256,uint${256 - length})`](0, ...args),
|
|
|
|
+ upperLookup: (...args) => this.mock.methods[`$upperLookup(uint256,uint${256 - length})`](0, ...args),
|
|
|
|
+ upperLookupRecent: (...args) =>
|
|
|
|
+ this.mock.methods[`$upperLookupRecent(uint256,uint${256 - length})`](0, ...args),
|
|
|
|
+ };
|
|
|
|
+ }
|
|
});
|
|
});
|
|
|
|
|
|
describe('History checkpoints', function () {
|
|
describe('History checkpoints', function () {
|
|
- const latest = (self, ...args) => self.methods['$latest_Checkpoints_History(uint256)'](0, ...args);
|
|
|
|
- const latestCheckpoint = (self, ...args) =>
|
|
|
|
- self.methods['$latestCheckpoint_Checkpoints_History(uint256)'](0, ...args);
|
|
|
|
- const push = (self, ...args) => self.methods['$push(uint256,uint256)'](0, ...args);
|
|
|
|
- const getAtBlock = (self, ...args) => self.methods['$getAtBlock(uint256,uint256)'](0, ...args);
|
|
|
|
- const getAtRecentBlock = (self, ...args) => self.methods['$getAtProbablyRecentBlock(uint256,uint256)'](0, ...args);
|
|
|
|
- const getLength = (self, ...args) => self.methods['$length_Checkpoints_History(uint256)'](0, ...args);
|
|
|
|
-
|
|
|
|
describe('without checkpoints', function () {
|
|
describe('without checkpoints', function () {
|
|
it('returns zero as latest value', async function () {
|
|
it('returns zero as latest value', async function () {
|
|
- expect(await latest(this.mock)).to.be.bignumber.equal('0');
|
|
|
|
|
|
+ expect(await this.methods.history.latest()).to.be.bignumber.equal('0');
|
|
|
|
|
|
- const ckpt = await latestCheckpoint(this.mock);
|
|
|
|
|
|
+ const ckpt = await this.methods.history.latestCheckpoint();
|
|
expect(ckpt[0]).to.be.equal(false);
|
|
expect(ckpt[0]).to.be.equal(false);
|
|
expect(ckpt[1]).to.be.bignumber.equal('0');
|
|
expect(ckpt[1]).to.be.bignumber.equal('0');
|
|
expect(ckpt[2]).to.be.bignumber.equal('0');
|
|
expect(ckpt[2]).to.be.bignumber.equal('0');
|
|
@@ -35,49 +55,63 @@ contract('Checkpoints', function () {
|
|
|
|
|
|
it('returns zero as past value', async function () {
|
|
it('returns zero as past value', async function () {
|
|
await time.advanceBlock();
|
|
await time.advanceBlock();
|
|
- expect(await getAtBlock(this.mock, (await web3.eth.getBlockNumber()) - 1)).to.be.bignumber.equal('0');
|
|
|
|
- expect(await getAtRecentBlock(this.mock, (await web3.eth.getBlockNumber()) - 1)).to.be.bignumber.equal('0');
|
|
|
|
|
|
+ expect(await this.methods.history.getAtBlock((await web3.eth.getBlockNumber()) - 1)).to.be.bignumber.equal('0');
|
|
|
|
+ expect(
|
|
|
|
+ await this.methods.history.getAtRecentBlock((await web3.eth.getBlockNumber()) - 1),
|
|
|
|
+ ).to.be.bignumber.equal('0');
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
|
|
describe('with checkpoints', function () {
|
|
describe('with checkpoints', function () {
|
|
beforeEach('pushing checkpoints', async function () {
|
|
beforeEach('pushing checkpoints', async function () {
|
|
- this.tx1 = await push(this.mock, 1);
|
|
|
|
- this.tx2 = await push(this.mock, 2);
|
|
|
|
|
|
+ this.tx1 = await this.methods.history.push(1);
|
|
|
|
+ this.tx2 = await this.methods.history.push(2);
|
|
await time.advanceBlock();
|
|
await time.advanceBlock();
|
|
- this.tx3 = await push(this.mock, 3);
|
|
|
|
|
|
+ this.tx3 = await this.methods.history.push(3);
|
|
await time.advanceBlock();
|
|
await time.advanceBlock();
|
|
await time.advanceBlock();
|
|
await time.advanceBlock();
|
|
});
|
|
});
|
|
|
|
|
|
it('returns latest value', async function () {
|
|
it('returns latest value', async function () {
|
|
- expect(await latest(this.mock)).to.be.bignumber.equal('3');
|
|
|
|
|
|
+ expect(await this.methods.history.latest()).to.be.bignumber.equal('3');
|
|
|
|
|
|
- const ckpt = await latestCheckpoint(this.mock);
|
|
|
|
|
|
+ const ckpt = await this.methods.history.latestCheckpoint();
|
|
expect(ckpt[0]).to.be.equal(true);
|
|
expect(ckpt[0]).to.be.equal(true);
|
|
expect(ckpt[1]).to.be.bignumber.equal(web3.utils.toBN(this.tx3.receipt.blockNumber));
|
|
expect(ckpt[1]).to.be.bignumber.equal(web3.utils.toBN(this.tx3.receipt.blockNumber));
|
|
expect(ckpt[2]).to.be.bignumber.equal(web3.utils.toBN('3'));
|
|
expect(ckpt[2]).to.be.bignumber.equal(web3.utils.toBN('3'));
|
|
});
|
|
});
|
|
|
|
|
|
- for (const getAtBlockVariant of [getAtBlock, getAtRecentBlock]) {
|
|
|
|
|
|
+ for (const getAtBlockVariant of ['getAtBlock', 'getAtRecentBlock']) {
|
|
describe(`lookup: ${getAtBlockVariant}`, function () {
|
|
describe(`lookup: ${getAtBlockVariant}`, function () {
|
|
it('returns past values', async function () {
|
|
it('returns past values', async function () {
|
|
- expect(await getAtBlockVariant(this.mock, this.tx1.receipt.blockNumber - 1)).to.be.bignumber.equal('0');
|
|
|
|
- expect(await getAtBlockVariant(this.mock, this.tx1.receipt.blockNumber)).to.be.bignumber.equal('1');
|
|
|
|
- expect(await getAtBlockVariant(this.mock, this.tx2.receipt.blockNumber)).to.be.bignumber.equal('2');
|
|
|
|
|
|
+ expect(
|
|
|
|
+ await this.methods.history[getAtBlockVariant](this.tx1.receipt.blockNumber - 1),
|
|
|
|
+ ).to.be.bignumber.equal('0');
|
|
|
|
+ expect(await this.methods.history[getAtBlockVariant](this.tx1.receipt.blockNumber)).to.be.bignumber.equal(
|
|
|
|
+ '1',
|
|
|
|
+ );
|
|
|
|
+ expect(await this.methods.history[getAtBlockVariant](this.tx2.receipt.blockNumber)).to.be.bignumber.equal(
|
|
|
|
+ '2',
|
|
|
|
+ );
|
|
// Block with no new checkpoints
|
|
// Block with no new checkpoints
|
|
- expect(await getAtBlockVariant(this.mock, this.tx2.receipt.blockNumber + 1)).to.be.bignumber.equal('2');
|
|
|
|
- expect(await getAtBlockVariant(this.mock, this.tx3.receipt.blockNumber)).to.be.bignumber.equal('3');
|
|
|
|
- expect(await getAtBlockVariant(this.mock, this.tx3.receipt.blockNumber + 1)).to.be.bignumber.equal('3');
|
|
|
|
|
|
+ expect(
|
|
|
|
+ await this.methods.history[getAtBlockVariant](this.tx2.receipt.blockNumber + 1),
|
|
|
|
+ ).to.be.bignumber.equal('2');
|
|
|
|
+ expect(await this.methods.history[getAtBlockVariant](this.tx3.receipt.blockNumber)).to.be.bignumber.equal(
|
|
|
|
+ '3',
|
|
|
|
+ );
|
|
|
|
+ expect(
|
|
|
|
+ await this.methods.history[getAtBlockVariant](this.tx3.receipt.blockNumber + 1),
|
|
|
|
+ ).to.be.bignumber.equal('3');
|
|
});
|
|
});
|
|
it('reverts if block number >= current block', async function () {
|
|
it('reverts if block number >= current block', async function () {
|
|
await expectRevert(
|
|
await expectRevert(
|
|
- getAtBlockVariant(this.mock, await web3.eth.getBlockNumber()),
|
|
|
|
|
|
+ this.methods.history[getAtBlockVariant](await web3.eth.getBlockNumber()),
|
|
'Checkpoints: block not yet mined',
|
|
'Checkpoints: block not yet mined',
|
|
);
|
|
);
|
|
|
|
|
|
await expectRevert(
|
|
await expectRevert(
|
|
- getAtBlockVariant(this.mock, (await web3.eth.getBlockNumber()) + 1),
|
|
|
|
|
|
+ this.methods.history[getAtBlockVariant]((await web3.eth.getBlockNumber()) + 1),
|
|
'Checkpoints: block not yet mined',
|
|
'Checkpoints: block not yet mined',
|
|
);
|
|
);
|
|
});
|
|
});
|
|
@@ -85,58 +119,48 @@ contract('Checkpoints', function () {
|
|
}
|
|
}
|
|
|
|
|
|
it('multiple checkpoints in the same block', async function () {
|
|
it('multiple checkpoints in the same block', async function () {
|
|
- const lengthBefore = await getLength(this.mock);
|
|
|
|
|
|
+ const lengthBefore = await this.methods.history.length();
|
|
|
|
|
|
await batchInBlock([
|
|
await batchInBlock([
|
|
- () => push(this.mock, 8, { gas: 100000 }),
|
|
|
|
- () => push(this.mock, 9, { gas: 100000 }),
|
|
|
|
- () => push(this.mock, 10, { gas: 100000 }),
|
|
|
|
|
|
+ () => this.methods.history.push(8, { gas: 100000 }),
|
|
|
|
+ () => this.methods.history.push(9, { gas: 100000 }),
|
|
|
|
+ () => this.methods.history.push(10, { gas: 100000 }),
|
|
]);
|
|
]);
|
|
|
|
|
|
- expect(await getLength(this.mock)).to.be.bignumber.equal(lengthBefore.addn(1));
|
|
|
|
- expect(await latest(this.mock)).to.be.bignumber.equal('10');
|
|
|
|
|
|
+ expect(await this.methods.history.length()).to.be.bignumber.equal(lengthBefore.addn(1));
|
|
|
|
+ expect(await this.methods.history.latest()).to.be.bignumber.equal('10');
|
|
});
|
|
});
|
|
|
|
|
|
it('more than 5 checkpoints', async function () {
|
|
it('more than 5 checkpoints', async function () {
|
|
for (let i = 4; i <= 6; i++) {
|
|
for (let i = 4; i <= 6; i++) {
|
|
- await push(this.mock, i);
|
|
|
|
|
|
+ await this.methods.history.push(i);
|
|
}
|
|
}
|
|
- expect(await getLength(this.mock)).to.be.bignumber.equal('6');
|
|
|
|
|
|
+ expect(await this.methods.history.length()).to.be.bignumber.equal('6');
|
|
const block = await web3.eth.getBlockNumber();
|
|
const block = await web3.eth.getBlockNumber();
|
|
// recent
|
|
// recent
|
|
- expect(await getAtRecentBlock(this.mock, block - 1)).to.be.bignumber.equal('5');
|
|
|
|
|
|
+ expect(await this.methods.history.getAtRecentBlock(block - 1)).to.be.bignumber.equal('5');
|
|
// non-recent
|
|
// non-recent
|
|
- expect(await getAtRecentBlock(this.mock, block - 9)).to.be.bignumber.equal('0');
|
|
|
|
|
|
+ expect(await this.methods.history.getAtRecentBlock(block - 9)).to.be.bignumber.equal('0');
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
|
|
- for (const length of [160, 224]) {
|
|
|
|
|
|
+ for (const length of traceLengths) {
|
|
describe(`Trace${length}`, function () {
|
|
describe(`Trace${length}`, function () {
|
|
- const latest = (self, ...args) => self.methods[`$latest_Checkpoints_Trace${length}(uint256)`](0, ...args);
|
|
|
|
- const latestCheckpoint = (self, ...args) =>
|
|
|
|
- self.methods[`$latestCheckpoint_Checkpoints_Trace${length}(uint256)`](0, ...args);
|
|
|
|
- const push = (self, ...args) => self.methods[`$push(uint256,uint${256 - length},uint${length})`](0, ...args);
|
|
|
|
- const lowerLookup = (self, ...args) => self.methods[`$lowerLookup(uint256,uint${256 - length})`](0, ...args);
|
|
|
|
- const upperLookup = (self, ...args) => self.methods[`$upperLookup(uint256,uint${256 - length})`](0, ...args);
|
|
|
|
- const upperLookupRecent = (self, ...args) =>
|
|
|
|
- self.methods[`$upperLookupRecent(uint256,uint${256 - length})`](0, ...args);
|
|
|
|
- const getLength = (self, ...args) => self.methods[`$length_Checkpoints_Trace${length}(uint256)`](0, ...args);
|
|
|
|
-
|
|
|
|
describe('without checkpoints', function () {
|
|
describe('without checkpoints', function () {
|
|
it('returns zero as latest value', async function () {
|
|
it('returns zero as latest value', async function () {
|
|
- expect(await latest(this.mock)).to.be.bignumber.equal('0');
|
|
|
|
|
|
+ expect(await this.methods.trace[length].latest()).to.be.bignumber.equal('0');
|
|
|
|
|
|
- const ckpt = await latestCheckpoint(this.mock);
|
|
|
|
|
|
+ const ckpt = await this.methods.trace[length].latestCheckpoint();
|
|
expect(ckpt[0]).to.be.equal(false);
|
|
expect(ckpt[0]).to.be.equal(false);
|
|
expect(ckpt[1]).to.be.bignumber.equal('0');
|
|
expect(ckpt[1]).to.be.bignumber.equal('0');
|
|
expect(ckpt[2]).to.be.bignumber.equal('0');
|
|
expect(ckpt[2]).to.be.bignumber.equal('0');
|
|
});
|
|
});
|
|
|
|
|
|
it('lookup returns 0', async function () {
|
|
it('lookup returns 0', async function () {
|
|
- expect(await lowerLookup(this.mock, 0)).to.be.bignumber.equal('0');
|
|
|
|
- expect(await upperLookup(this.mock, 0)).to.be.bignumber.equal('0');
|
|
|
|
- expect(await upperLookupRecent(this.mock, 0)).to.be.bignumber.equal('0');
|
|
|
|
|
|
+ expect(await this.methods.trace[length].lowerLookup(0)).to.be.bignumber.equal('0');
|
|
|
|
+ expect(await this.methods.trace[length].upperLookup(0)).to.be.bignumber.equal('0');
|
|
|
|
+ expect(await this.methods.trace[length].upperLookupRecent(0)).to.be.bignumber.equal('0');
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
|
|
@@ -150,46 +174,49 @@ contract('Checkpoints', function () {
|
|
{ key: '11', value: '99' },
|
|
{ key: '11', value: '99' },
|
|
];
|
|
];
|
|
for (const { key, value } of this.checkpoints) {
|
|
for (const { key, value } of this.checkpoints) {
|
|
- await push(this.mock, key, value);
|
|
|
|
|
|
+ await this.methods.trace[length].push(key, value);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
it('length', async function () {
|
|
it('length', async function () {
|
|
- expect(await getLength(this.mock)).to.be.bignumber.equal(this.checkpoints.length.toString());
|
|
|
|
|
|
+ expect(await this.methods.trace[length].length()).to.be.bignumber.equal(this.checkpoints.length.toString());
|
|
});
|
|
});
|
|
|
|
|
|
it('returns latest value', async function () {
|
|
it('returns latest value', async function () {
|
|
- expect(await latest(this.mock)).to.be.bignumber.equal(last(this.checkpoints).value);
|
|
|
|
|
|
+ expect(await this.methods.trace[length].latest()).to.be.bignumber.equal(last(this.checkpoints).value);
|
|
|
|
|
|
- const ckpt = await latestCheckpoint(this.mock);
|
|
|
|
|
|
+ const ckpt = await this.methods.trace[length].latestCheckpoint();
|
|
expect(ckpt[0]).to.be.equal(true);
|
|
expect(ckpt[0]).to.be.equal(true);
|
|
expect(ckpt[1]).to.be.bignumber.equal(last(this.checkpoints).key);
|
|
expect(ckpt[1]).to.be.bignumber.equal(last(this.checkpoints).key);
|
|
expect(ckpt[2]).to.be.bignumber.equal(last(this.checkpoints).value);
|
|
expect(ckpt[2]).to.be.bignumber.equal(last(this.checkpoints).value);
|
|
});
|
|
});
|
|
|
|
|
|
it('cannot push values in the past', async function () {
|
|
it('cannot push values in the past', async function () {
|
|
- await expectRevert(push(this.mock, last(this.checkpoints).key - 1, '0'), 'Checkpoint: decreasing keys');
|
|
|
|
|
|
+ await expectRevert(
|
|
|
|
+ this.methods.trace[length].push(last(this.checkpoints).key - 1, '0'),
|
|
|
|
+ 'Checkpoint: decreasing keys',
|
|
|
|
+ );
|
|
});
|
|
});
|
|
|
|
|
|
it('can update last value', async function () {
|
|
it('can update last value', async function () {
|
|
const newValue = '42';
|
|
const newValue = '42';
|
|
|
|
|
|
// check length before the update
|
|
// check length before the update
|
|
- expect(await getLength(this.mock)).to.be.bignumber.equal(this.checkpoints.length.toString());
|
|
|
|
|
|
+ expect(await this.methods.trace[length].length()).to.be.bignumber.equal(this.checkpoints.length.toString());
|
|
|
|
|
|
// update last key
|
|
// update last key
|
|
- await push(this.mock, last(this.checkpoints).key, newValue);
|
|
|
|
- expect(await latest(this.mock)).to.be.bignumber.equal(newValue);
|
|
|
|
|
|
+ await this.methods.trace[length].push(last(this.checkpoints).key, newValue);
|
|
|
|
+ expect(await this.methods.trace[length].latest()).to.be.bignumber.equal(newValue);
|
|
|
|
|
|
// check that length did not change
|
|
// check that length did not change
|
|
- expect(await getLength(this.mock)).to.be.bignumber.equal(this.checkpoints.length.toString());
|
|
|
|
|
|
+ expect(await this.methods.trace[length].length()).to.be.bignumber.equal(this.checkpoints.length.toString());
|
|
});
|
|
});
|
|
|
|
|
|
it('lower lookup', async function () {
|
|
it('lower lookup', async function () {
|
|
for (let i = 0; i < 14; ++i) {
|
|
for (let i = 0; i < 14; ++i) {
|
|
const value = first(this.checkpoints.filter(x => i <= x.key))?.value || '0';
|
|
const value = first(this.checkpoints.filter(x => i <= x.key))?.value || '0';
|
|
|
|
|
|
- expect(await lowerLookup(this.mock, i)).to.be.bignumber.equal(value);
|
|
|
|
|
|
+ expect(await this.methods.trace[length].lowerLookup(i)).to.be.bignumber.equal(value);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
@@ -197,8 +224,8 @@ contract('Checkpoints', function () {
|
|
for (let i = 0; i < 14; ++i) {
|
|
for (let i = 0; i < 14; ++i) {
|
|
const value = last(this.checkpoints.filter(x => i >= x.key))?.value || '0';
|
|
const value = last(this.checkpoints.filter(x => i >= x.key))?.value || '0';
|
|
|
|
|
|
- expect(await upperLookup(this.mock, i)).to.be.bignumber.equal(value);
|
|
|
|
- expect(await upperLookupRecent(this.mock, i)).to.be.bignumber.equal(value);
|
|
|
|
|
|
+ expect(await this.methods.trace[length].upperLookup(i)).to.be.bignumber.equal(value);
|
|
|
|
+ expect(await this.methods.trace[length].upperLookupRecent(i)).to.be.bignumber.equal(value);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
@@ -213,13 +240,13 @@ contract('Checkpoints', function () {
|
|
const allCheckpoints = [].concat(this.checkpoints, moreCheckpoints);
|
|
const allCheckpoints = [].concat(this.checkpoints, moreCheckpoints);
|
|
|
|
|
|
for (const { key, value } of moreCheckpoints) {
|
|
for (const { key, value } of moreCheckpoints) {
|
|
- await push(this.mock, key, value);
|
|
|
|
|
|
+ await this.methods.trace[length].push(key, value);
|
|
}
|
|
}
|
|
|
|
|
|
for (let i = 0; i < 25; ++i) {
|
|
for (let i = 0; i < 25; ++i) {
|
|
const value = last(allCheckpoints.filter(x => i >= x.key))?.value || '0';
|
|
const value = last(allCheckpoints.filter(x => i >= x.key))?.value || '0';
|
|
- expect(await upperLookup(this.mock, i)).to.be.bignumber.equal(value);
|
|
|
|
- expect(await upperLookupRecent(this.mock, i)).to.be.bignumber.equal(value);
|
|
|
|
|
|
+ expect(await this.methods.trace[length].upperLookup(i)).to.be.bignumber.equal(value);
|
|
|
|
+ expect(await this.methods.trace[length].upperLookupRecent(i)).to.be.bignumber.equal(value);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
});
|
|
});
|