2021-11-01 11:30:35 +00:00
|
|
|
const { expect } = require("chai")
|
|
|
|
const { ethers } = require("hardhat")
|
2022-03-08 14:58:08 +00:00
|
|
|
const {
|
|
|
|
snapshot,
|
|
|
|
revert,
|
2022-03-09 10:21:19 +00:00
|
|
|
ensureMinimumBlockHeight,
|
2022-03-08 14:58:08 +00:00
|
|
|
currentTime,
|
|
|
|
advanceTime,
|
|
|
|
advanceTimeTo,
|
|
|
|
} = require("./evm")
|
2022-03-09 10:11:01 +00:00
|
|
|
const { periodic } = require("./time")
|
2021-11-01 11:30:35 +00:00
|
|
|
|
|
|
|
describe("Proofs", function () {
|
|
|
|
const id = ethers.utils.randomBytes(32)
|
|
|
|
const period = 10
|
|
|
|
const timeout = 5
|
2022-03-10 09:19:21 +00:00
|
|
|
const downtime = 64
|
2022-03-08 14:58:08 +00:00
|
|
|
const duration = 1000
|
2022-03-10 09:12:03 +00:00
|
|
|
const probability = 4 // require a proof roughly once every 4 periods
|
2021-11-01 11:30:35 +00:00
|
|
|
|
|
|
|
let proofs
|
|
|
|
|
|
|
|
beforeEach(async function () {
|
2022-03-08 14:58:08 +00:00
|
|
|
await snapshot()
|
2022-03-09 10:21:19 +00:00
|
|
|
await ensureMinimumBlockHeight(256)
|
2021-11-01 11:30:35 +00:00
|
|
|
const Proofs = await ethers.getContractFactory("TestProofs")
|
2022-03-10 09:19:21 +00:00
|
|
|
proofs = await Proofs.deploy(period, timeout, downtime)
|
2021-11-01 11:30:35 +00:00
|
|
|
})
|
|
|
|
|
2022-03-08 14:58:08 +00:00
|
|
|
afterEach(async function () {
|
|
|
|
await revert()
|
|
|
|
})
|
|
|
|
|
|
|
|
it("calculates an end time based on duration", async function () {
|
|
|
|
await proofs.expectProofs(id, probability, duration)
|
|
|
|
let end = (await currentTime()) + duration
|
|
|
|
expect((await proofs.end(id)).toNumber()).to.be.closeTo(end, 1)
|
2021-11-03 12:24:50 +00:00
|
|
|
})
|
|
|
|
|
2022-02-09 13:17:23 +00:00
|
|
|
it("does not allow ids to be reused", async function () {
|
2022-03-08 14:58:08 +00:00
|
|
|
await proofs.expectProofs(id, probability, duration)
|
|
|
|
await expect(
|
|
|
|
proofs.expectProofs(id, probability, duration)
|
|
|
|
).to.be.revertedWith("Proof id already in use")
|
2021-11-01 14:28:22 +00:00
|
|
|
})
|
|
|
|
|
2022-03-08 14:58:08 +00:00
|
|
|
it("requires proofs with an agreed upon probability", async function () {
|
|
|
|
const duration = 100_000
|
|
|
|
await proofs.expectProofs(id, probability, duration)
|
2021-11-03 16:02:12 +00:00
|
|
|
let amount = 0
|
2022-03-08 14:58:08 +00:00
|
|
|
for (let i = 0; i < 100; i++) {
|
|
|
|
if (await proofs.isProofRequired(id)) {
|
2021-11-03 16:02:12 +00:00
|
|
|
amount += 1
|
|
|
|
}
|
2022-03-08 14:58:08 +00:00
|
|
|
await advanceTime(period)
|
2021-11-03 16:02:12 +00:00
|
|
|
}
|
2022-03-10 09:12:03 +00:00
|
|
|
let expected = 100 / probability
|
2022-03-08 14:58:08 +00:00
|
|
|
expect(amount).to.be.closeTo(expected, expected / 2)
|
2021-11-03 16:02:12 +00:00
|
|
|
})
|
|
|
|
|
2022-03-08 14:58:08 +00:00
|
|
|
it("requires no proofs in the start period", async function () {
|
|
|
|
const startPeriod = Math.floor((await currentTime()) / period)
|
|
|
|
const probability = 1
|
|
|
|
await proofs.expectProofs(id, probability, duration)
|
|
|
|
while (Math.floor((await currentTime()) / period) == startPeriod) {
|
|
|
|
expect(await proofs.isProofRequired(id)).to.be.false
|
|
|
|
await advanceTime(1)
|
2021-11-03 16:15:03 +00:00
|
|
|
}
|
2022-03-08 14:58:08 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
it("requires no proofs in the end period", async function () {
|
|
|
|
const probability = 1
|
|
|
|
await proofs.expectProofs(id, probability, duration)
|
|
|
|
await advanceTime(duration)
|
|
|
|
expect(await proofs.isProofRequired(id)).to.be.false
|
|
|
|
})
|
|
|
|
|
|
|
|
it("requires no proofs after the end time", async function () {
|
|
|
|
const probability = 1
|
|
|
|
await proofs.expectProofs(id, probability, duration)
|
|
|
|
await advanceTime(duration + timeout)
|
|
|
|
expect(await proofs.isProofRequired(id)).to.be.false
|
|
|
|
})
|
|
|
|
|
|
|
|
it("requires proofs for different ids at different times", async function () {
|
|
|
|
let id1 = ethers.utils.randomBytes(32)
|
|
|
|
let id2 = ethers.utils.randomBytes(32)
|
|
|
|
let id3 = ethers.utils.randomBytes(32)
|
|
|
|
for (let id of [id1, id2, id3]) {
|
|
|
|
await proofs.expectProofs(id, probability, duration)
|
|
|
|
}
|
|
|
|
let req1, req2, req3
|
|
|
|
while (req1 === req2 && req2 === req3) {
|
|
|
|
req1 = await proofs.isProofRequired(id1)
|
|
|
|
req2 = await proofs.isProofRequired(id2)
|
|
|
|
req3 = await proofs.isProofRequired(id3)
|
|
|
|
await advanceTime(period)
|
2021-11-03 16:15:03 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2021-11-01 11:30:35 +00:00
|
|
|
describe("when proofs are required", async function () {
|
2022-03-09 10:11:01 +00:00
|
|
|
const { periodOf, periodEnd } = periodic(period)
|
|
|
|
|
2021-11-01 11:30:35 +00:00
|
|
|
beforeEach(async function () {
|
2022-03-08 14:58:08 +00:00
|
|
|
await proofs.expectProofs(id, probability, duration)
|
2021-11-01 11:30:35 +00:00
|
|
|
})
|
|
|
|
|
2022-03-08 14:58:08 +00:00
|
|
|
async function waitUntilProofIsRequired(id) {
|
2022-03-09 10:29:53 +00:00
|
|
|
await advanceTimeTo(periodEnd(periodOf(await currentTime())))
|
2022-03-08 14:58:08 +00:00
|
|
|
while (!(await proofs.isProofRequired(id))) {
|
|
|
|
await advanceTime(period)
|
2021-11-01 11:30:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-10 12:04:46 +00:00
|
|
|
it("provides different challenges per period", async function () {
|
|
|
|
await waitUntilProofIsRequired(id)
|
|
|
|
const challenge1 = await proofs.getChallenge(id)
|
|
|
|
await waitUntilProofIsRequired(id)
|
|
|
|
const challenge2 = await proofs.getChallenge(id)
|
|
|
|
expect(challenge2).not.to.equal(challenge1)
|
|
|
|
})
|
|
|
|
|
|
|
|
it("provides different challenges per id", async function () {
|
|
|
|
const id2 = ethers.utils.randomBytes(32)
|
|
|
|
const id3 = ethers.utils.randomBytes(32)
|
|
|
|
const challenge1 = await proofs.getChallenge(id)
|
|
|
|
const challenge2 = await proofs.getChallenge(id2)
|
|
|
|
const challenge3 = await proofs.getChallenge(id3)
|
|
|
|
expect(challenge1 === challenge2 && challenge2 === challenge3).to.be.false
|
|
|
|
})
|
|
|
|
|
2021-11-01 11:30:35 +00:00
|
|
|
it("submits a correct proof", async function () {
|
2022-03-08 14:58:08 +00:00
|
|
|
await proofs.submitProof(id, true)
|
2021-11-01 11:30:35 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
it("fails proof submission when proof is incorrect", async function () {
|
2022-03-08 14:58:08 +00:00
|
|
|
await expect(proofs.submitProof(id, false)).to.be.revertedWith(
|
|
|
|
"Invalid proof"
|
|
|
|
)
|
2021-11-01 11:30:35 +00:00
|
|
|
})
|
|
|
|
|
2022-02-09 13:17:23 +00:00
|
|
|
it("fails proof submission when already submitted", async function () {
|
2022-03-08 14:58:08 +00:00
|
|
|
await advanceTimeTo(periodEnd(periodOf(await currentTime())))
|
|
|
|
await proofs.submitProof(id, true)
|
|
|
|
await expect(proofs.submitProof(id, true)).to.be.revertedWith(
|
|
|
|
"Proof already submitted"
|
|
|
|
)
|
2021-11-01 11:30:35 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
it("marks a proof as missing", async function () {
|
|
|
|
expect(await proofs.missed(id)).to.equal(0)
|
2022-03-08 14:58:08 +00:00
|
|
|
await waitUntilProofIsRequired(id)
|
|
|
|
let missedPeriod = periodOf(await currentTime())
|
|
|
|
await advanceTimeTo(periodEnd(missedPeriod))
|
|
|
|
await proofs.markProofAsMissing(id, missedPeriod)
|
2021-11-01 11:30:35 +00:00
|
|
|
expect(await proofs.missed(id)).to.equal(1)
|
|
|
|
})
|
|
|
|
|
2022-03-08 14:58:08 +00:00
|
|
|
it("does not mark a proof as missing before period end", async function () {
|
|
|
|
await waitUntilProofIsRequired(id)
|
|
|
|
let currentPeriod = periodOf(await currentTime())
|
|
|
|
await expect(
|
|
|
|
proofs.markProofAsMissing(id, currentPeriod)
|
|
|
|
).to.be.revertedWith("Period has not ended yet")
|
|
|
|
})
|
|
|
|
|
|
|
|
it("does not mark a proof as missing after timeout", async function () {
|
|
|
|
await waitUntilProofIsRequired(id)
|
|
|
|
let currentPeriod = periodOf(await currentTime())
|
|
|
|
await advanceTimeTo(periodEnd(currentPeriod) + timeout)
|
2021-11-01 11:30:35 +00:00
|
|
|
await expect(
|
2022-03-08 14:58:08 +00:00
|
|
|
proofs.markProofAsMissing(id, currentPeriod)
|
|
|
|
).to.be.revertedWith("Validation timed out")
|
2021-11-01 11:30:35 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
it("does not mark a submitted proof as missing", async function () {
|
2022-03-08 14:58:08 +00:00
|
|
|
await waitUntilProofIsRequired(id)
|
|
|
|
let submittedPeriod = periodOf(await currentTime())
|
|
|
|
await proofs.submitProof(id, true)
|
|
|
|
await advanceTimeTo(periodEnd(submittedPeriod))
|
2021-11-01 11:30:35 +00:00
|
|
|
await expect(
|
2022-03-08 14:58:08 +00:00
|
|
|
proofs.markProofAsMissing(id, submittedPeriod)
|
2021-11-01 11:30:35 +00:00
|
|
|
).to.be.revertedWith("Proof was submitted, not missing")
|
|
|
|
})
|
|
|
|
|
|
|
|
it("does not mark proof as missing when not required", async function () {
|
2022-03-08 14:58:08 +00:00
|
|
|
while (await proofs.isProofRequired(id)) {
|
|
|
|
await advanceTime(period)
|
2021-11-01 11:30:35 +00:00
|
|
|
}
|
2022-03-08 14:58:08 +00:00
|
|
|
let currentPeriod = periodOf(await currentTime())
|
|
|
|
await advanceTimeTo(periodEnd(currentPeriod))
|
2021-11-01 11:30:35 +00:00
|
|
|
await expect(
|
2022-03-08 14:58:08 +00:00
|
|
|
proofs.markProofAsMissing(id, currentPeriod)
|
2021-11-01 11:30:35 +00:00
|
|
|
).to.be.revertedWith("Proof was not required")
|
|
|
|
})
|
|
|
|
|
|
|
|
it("does not mark proof as missing twice", async function () {
|
2022-03-08 14:58:08 +00:00
|
|
|
await waitUntilProofIsRequired(id)
|
|
|
|
let missedPeriod = periodOf(await currentTime())
|
|
|
|
await advanceTimeTo(periodEnd(missedPeriod))
|
|
|
|
await proofs.markProofAsMissing(id, missedPeriod)
|
2021-11-01 11:30:35 +00:00
|
|
|
await expect(
|
2022-03-08 14:58:08 +00:00
|
|
|
proofs.markProofAsMissing(id, missedPeriod)
|
2021-11-01 11:30:35 +00:00
|
|
|
).to.be.revertedWith("Proof already marked as missing")
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|