2021-11-01 11:30:35 +00:00
|
|
|
const { expect } = require("chai")
|
|
|
|
const { ethers } = require("hardhat")
|
|
|
|
|
|
|
|
describe("Proofs", function () {
|
|
|
|
|
|
|
|
const id = ethers.utils.randomBytes(32)
|
|
|
|
const period = 10
|
|
|
|
const timeout = 5
|
2021-11-03 16:02:12 +00:00
|
|
|
const duration = 50
|
2021-11-01 11:30:35 +00:00
|
|
|
|
|
|
|
let proofs
|
|
|
|
|
|
|
|
beforeEach(async function () {
|
|
|
|
const Proofs = await ethers.getContractFactory("TestProofs")
|
|
|
|
proofs = await Proofs.deploy()
|
|
|
|
})
|
|
|
|
|
2021-11-03 12:24:50 +00:00
|
|
|
async function mineBlock() {
|
|
|
|
await ethers.provider.send("evm_mine")
|
|
|
|
}
|
|
|
|
|
|
|
|
async function minedBlockNumber() {
|
|
|
|
return await ethers.provider.getBlockNumber()
|
|
|
|
}
|
|
|
|
|
2021-11-01 11:30:35 +00:00
|
|
|
it("indicates that proofs are required", async function() {
|
2021-11-03 12:24:50 +00:00
|
|
|
await proofs.expectProofs(id, period, timeout, duration)
|
2021-11-01 11:30:35 +00:00
|
|
|
expect(await proofs.period(id)).to.equal(period)
|
|
|
|
expect(await proofs.timeout(id)).to.equal(timeout)
|
|
|
|
})
|
|
|
|
|
2021-11-03 12:24:50 +00:00
|
|
|
it("calculates an endtime based on duration and timeout", async function() {
|
|
|
|
await proofs.expectProofs(id, period, timeout, duration)
|
|
|
|
let start = await minedBlockNumber()
|
|
|
|
let end = start + duration + 2 * timeout
|
|
|
|
expect(await proofs.end(id)).to.equal(end)
|
|
|
|
})
|
|
|
|
|
2021-11-01 11:30:35 +00:00
|
|
|
it("does not allow ids to be reused", async function() {
|
2021-11-03 12:24:50 +00:00
|
|
|
await proofs.expectProofs(id, period, timeout, duration)
|
2021-11-01 11:30:35 +00:00
|
|
|
await expect(
|
2021-11-03 12:24:50 +00:00
|
|
|
proofs.expectProofs(id, period, timeout, duration)
|
2021-11-01 11:30:35 +00:00
|
|
|
).to.be.revertedWith("Proof id already in use")
|
|
|
|
})
|
|
|
|
|
2021-11-01 14:28:22 +00:00
|
|
|
it("does not allow a proof timeout that is too large", async function () {
|
|
|
|
let invalidTimeout = 129 // max proof timeout is 128 blocks
|
|
|
|
await expect(
|
2021-11-03 12:24:50 +00:00
|
|
|
proofs.expectProofs(id, period, invalidTimeout, duration)
|
2021-11-01 14:28:22 +00:00
|
|
|
).to.be.revertedWith("Invalid proof timeout")
|
|
|
|
})
|
|
|
|
|
2021-11-03 16:02:12 +00:00
|
|
|
it("requires on average a proof every period", async function () {
|
|
|
|
let blocks = 500
|
|
|
|
let amount = 0
|
|
|
|
await proofs.expectProofs(id, period, timeout, blocks)
|
|
|
|
for (let i=0; i<blocks; i++) {
|
|
|
|
await mineBlock()
|
|
|
|
if (await proofs.isProofRequired(id, await minedBlockNumber())) {
|
|
|
|
amount += 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let average = blocks / amount
|
|
|
|
expect(average).to.be.closeTo(period, period / 2)
|
|
|
|
})
|
|
|
|
|
2021-11-01 11:30:35 +00:00
|
|
|
describe("when proofs are required", async function () {
|
|
|
|
|
|
|
|
beforeEach(async function () {
|
2021-11-03 12:24:50 +00:00
|
|
|
await proofs.expectProofs(id, period, timeout, duration)
|
2021-11-01 11:30:35 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
async function mineUntilProofIsRequired(id) {
|
|
|
|
while (!await proofs.isProofRequired(id, await minedBlockNumber())) {
|
|
|
|
mineBlock()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function mineUntilProofTimeout() {
|
|
|
|
for (let i=0; i<timeout; i++) {
|
|
|
|
mineBlock()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-03 16:02:12 +00:00
|
|
|
async function mineUntilEnd() {
|
|
|
|
const end = await proofs.end(id)
|
|
|
|
while (await minedBlockNumber() < end) {
|
|
|
|
mineBlock()
|
2021-11-01 11:30:35 +00:00
|
|
|
}
|
2021-11-03 16:02:12 +00:00
|
|
|
}
|
2021-11-01 11:30:35 +00:00
|
|
|
|
|
|
|
it("requires no proof for blocks that are unavailable", async function () {
|
|
|
|
await mineUntilProofIsRequired(id)
|
|
|
|
let blocknumber = await minedBlockNumber()
|
2021-11-03 16:02:12 +00:00
|
|
|
for (let i=0; i<256; i++) { // only last 256 blocks available in solidity
|
2021-11-01 11:30:35 +00:00
|
|
|
mineBlock()
|
|
|
|
}
|
|
|
|
expect(await proofs.isProofRequired(id, blocknumber)).to.be.false
|
|
|
|
})
|
|
|
|
|
2021-11-03 16:02:12 +00:00
|
|
|
it("requires no proof after end time", async function () {
|
|
|
|
await mineUntilEnd()
|
|
|
|
for (let i=0; i<4*period; i++) {
|
|
|
|
const blocknumber = await minedBlockNumber()
|
|
|
|
expect(await proofs.isProofRequired(id, blocknumber)).to.be.false
|
|
|
|
mineBlock()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2021-11-01 11:30:35 +00:00
|
|
|
it("submits a correct proof", async function () {
|
|
|
|
await mineUntilProofIsRequired(id)
|
|
|
|
let blocknumber = await minedBlockNumber()
|
|
|
|
await proofs.submitProof(id, blocknumber, true)
|
|
|
|
})
|
|
|
|
|
|
|
|
it("fails proof submission when proof is incorrect", async function () {
|
|
|
|
await mineUntilProofIsRequired(id)
|
|
|
|
let blocknumber = await minedBlockNumber()
|
|
|
|
await expect(
|
|
|
|
proofs.submitProof(id, blocknumber, false)
|
|
|
|
).to.be.revertedWith("Invalid proof")
|
|
|
|
})
|
|
|
|
|
|
|
|
it("fails proof submission when proof was not required", async function () {
|
|
|
|
while (await proofs.isProofRequired(id, await minedBlockNumber())) {
|
|
|
|
await mineBlock()
|
|
|
|
}
|
|
|
|
let blocknumber = await minedBlockNumber()
|
|
|
|
await expect(
|
|
|
|
proofs.submitProof(id, blocknumber, true)
|
|
|
|
).to.be.revertedWith("No proof required")
|
|
|
|
})
|
|
|
|
|
|
|
|
it("fails proof submission when proof is too late", async function () {
|
|
|
|
await mineUntilProofIsRequired(id)
|
|
|
|
let blocknumber = await minedBlockNumber()
|
|
|
|
await mineUntilProofTimeout()
|
|
|
|
await expect(
|
|
|
|
proofs.submitProof(id, blocknumber, true)
|
|
|
|
).to.be.revertedWith("Proof not allowed after timeout")
|
|
|
|
})
|
|
|
|
|
|
|
|
it("fails proof submission when already submitted", async function() {
|
|
|
|
await mineUntilProofIsRequired(id)
|
|
|
|
let blocknumber = await minedBlockNumber()
|
|
|
|
await proofs.submitProof(id, blocknumber, true)
|
|
|
|
await expect(
|
|
|
|
proofs.submitProof(id, blocknumber, true)
|
|
|
|
).to.be.revertedWith("Proof already submitted")
|
|
|
|
})
|
|
|
|
|
|
|
|
it("marks a proof as missing", async function () {
|
|
|
|
expect(await proofs.missed(id)).to.equal(0)
|
|
|
|
await mineUntilProofIsRequired(id)
|
|
|
|
let blocknumber = await minedBlockNumber()
|
|
|
|
await mineUntilProofTimeout()
|
|
|
|
await proofs.markProofAsMissing(id, blocknumber)
|
|
|
|
expect(await proofs.missed(id)).to.equal(1)
|
|
|
|
})
|
|
|
|
|
|
|
|
it("does not mark a proof as missing before timeout", async function () {
|
|
|
|
await mineUntilProofIsRequired(id)
|
|
|
|
let blocknumber = await minedBlockNumber()
|
|
|
|
await mineBlock()
|
|
|
|
await expect(
|
|
|
|
proofs.markProofAsMissing(id, blocknumber)
|
|
|
|
).to.be.revertedWith("Proof has not timed out yet")
|
|
|
|
})
|
|
|
|
|
|
|
|
it("does not mark a submitted proof as missing", async function () {
|
|
|
|
await mineUntilProofIsRequired(id)
|
|
|
|
let blocknumber = await minedBlockNumber()
|
|
|
|
await proofs.submitProof(id, blocknumber, true)
|
|
|
|
await mineUntilProofTimeout()
|
|
|
|
await expect(
|
|
|
|
proofs.markProofAsMissing(id, blocknumber)
|
|
|
|
).to.be.revertedWith("Proof was submitted, not missing")
|
|
|
|
})
|
|
|
|
|
|
|
|
it("does not mark proof as missing when not required", async function () {
|
|
|
|
while (await proofs.isProofRequired(id, await minedBlockNumber())) {
|
|
|
|
mineBlock()
|
|
|
|
}
|
|
|
|
let blocknumber = await minedBlockNumber()
|
|
|
|
await mineUntilProofTimeout()
|
|
|
|
await expect(
|
|
|
|
proofs.markProofAsMissing(id, blocknumber)
|
|
|
|
).to.be.revertedWith("Proof was not required")
|
|
|
|
})
|
|
|
|
|
|
|
|
it("does not mark proof as missing twice", async function () {
|
|
|
|
await mineUntilProofIsRequired(id)
|
|
|
|
let blocknumber = await minedBlockNumber()
|
|
|
|
await mineUntilProofTimeout()
|
|
|
|
await proofs.markProofAsMissing(id, blocknumber)
|
|
|
|
await expect(
|
|
|
|
proofs.markProofAsMissing(id, blocknumber)
|
|
|
|
).to.be.revertedWith("Proof already marked as missing")
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|