feat(tests): calculate maximum deviation for gas estimates

This commit is contained in:
Mark Spanbroek 2025-06-16 15:18:49 +02:00 committed by Adam Uhlíř
parent 7f844817ab
commit 78334edccf
No known key found for this signature in database
GPG Key ID: 1D17A9E81F76155B

171
test/GasUsage.test.js Normal file
View File

@ -0,0 +1,171 @@
const { ethers } = require("hardhat")
const {
exampleRequest,
exampleProof,
exampleConfiguration,
} = require("./examples")
const { maxPrice } = require("./price")
const { collateralPerSlot } = require("./collateral")
const { requestId, slotId } = require("./ids")
const {
revert,
snapshot,
ensureMinimumBlockHeight,
advanceTimeTo,
currentTime,
} = require("./evm")
const { expect } = require("chai")
const { patchOverloads } = require("./marketplace")
const { periodic } = require("./time")
const { RequestState } = require("./requests")
// Ensures that the actual gas costs can never deviate from the gas estimate by
// more than a certain percentage.
// The percentages from these tests should be used to pad the gas estimates in
// market.nim, for example when calling fillSlot:
// https://github.com/codex-storage/nim-codex/blob/6db6bf5f72a0038b77d02f48dcf128b4d77b469a/codex/contracts/market.nim#L278
describe("Marketplace gas estimates", function () {
let marketplace
let token
let signer
async function setupToken() {
const Token = await ethers.getContractFactory("TestToken")
const token = await Token.deploy()
await token.mint(signer.address, 1_000_000_000_000_000)
return token
}
async function setupMarketplace() {
const Marketplace = await ethers.getContractFactory("Marketplace")
const Verifier = await ethers.getContractFactory("TestVerifier")
const verifier = await Verifier.deploy()
await ensureMinimumBlockHeight(256)
const marketplace = await Marketplace.deploy(
exampleConfiguration(),
token.address,
verifier.address
)
patchOverloads(marketplace)
return marketplace
}
async function requestStorage() {
const request = exampleRequest()
request.client = signer.address
await token.approve(marketplace.address, maxPrice(request))
await marketplace.requestStorage(request)
return request
}
async function startRequest(request) {
const id = requestId(request)
for (let i = 0; i < request.ask.slots; i++) {
await marketplace.reserveSlot(id, i)
await token.approve(marketplace.address, collateralPerSlot(request))
await marketplace.fillSlot(id, i, exampleProof())
}
}
beforeEach(async function () {
await snapshot()
signer = (await ethers.getSigners())[0]
token = await setupToken()
marketplace = await setupMarketplace()
})
afterEach(async function () {
await revert()
})
describe("reserveSlot", function () {
it("has at most 1% deviation in gas usage", async function () {
const request = await requestStorage()
const id = requestId(request)
const gasUsage = []
for (let i = 0; i < request.ask.slots; i++) {
const transaction = await marketplace.reserveSlot(id, i)
const receipt = await transaction.wait()
gasUsage.push(receipt.gasUsed.toNumber())
await token.approve(marketplace.address, collateralPerSlot(request))
await marketplace.fillSlot(id, i, exampleProof())
}
const deviation = Math.max(...gasUsage) / Math.min(...gasUsage) - 1.0
expect(deviation).to.be.gt(0)
expect(deviation).to.be.lte(0.01)
})
})
describe("fillSlot", function () {
it("has at most 10% deviation in gas usage", async function () {
const request = await requestStorage()
const id = requestId(request)
const gasUsage = []
for (let i = 0; i < request.ask.slots; i++) {
await marketplace.reserveSlot(id, i)
await token.approve(marketplace.address, collateralPerSlot(request))
const transaction = await marketplace.fillSlot(id, i, exampleProof())
const receipt = await transaction.wait()
gasUsage.push(receipt.gasUsed.toNumber())
}
const deviation = Math.max(...gasUsage) / Math.min(...gasUsage) - 1.0
expect(deviation).to.be.gt(0)
expect(deviation).to.be.lte(0.1)
})
})
describe("freeSlot", function () {
it("has at most 200% deviation in gas usage", async function () {
const request = await requestStorage()
const id = requestId(request)
await startRequest(request)
const gasUsage = []
for (let i = 0; i < request.ask.slots; i++) {
const slot = { request: id, index: i }
const transaction = await marketplace.freeSlot(slotId(slot))
const receipt = await transaction.wait()
gasUsage.push(receipt.gasUsed.toNumber())
}
const deviation = Math.max(...gasUsage) / Math.min(...gasUsage) - 1.0
expect(deviation).to.be.gt(0)
expect(deviation).to.be.lte(2.0)
})
})
describe("markProofAsMissing", function () {
let period, periodOf, periodEnd
beforeEach(async function () {
const configuration = await marketplace.configuration()
period = configuration.proofs.period
;({ periodOf, periodEnd } = periodic(period))
})
it("has at most 50% deviation in gas usage", async function () {
const request = await requestStorage()
const id = requestId(request)
await startRequest(request)
const gasUsage = []
while ((await marketplace.requestState(id)) != RequestState.Failed) {
const missingPeriod = periodOf(await currentTime())
await advanceTimeTo(periodEnd(missingPeriod) + 1)
for (let i = 0; i < request.ask.slots; i++) {
try {
const slot = { request: id, index: i }
const transaction = await marketplace.markProofAsMissing(
slotId(slot),
missingPeriod
)
const receipt = await transaction.wait()
gasUsage.push(receipt.gasUsed.toNumber())
} catch (exception) {
// ignore: proof might not be missing
}
}
}
const deviation = Math.max(...gasUsage) / Math.min(...gasUsage) - 1.0
expect(deviation).to.be.gt(0)
expect(deviation).to.be.lte(0.5)
})
})
})