Replace Proof implementation with new design

More info: https://github.com/status-im/dagger-research/pull/66
This commit is contained in:
Mark Spanbroek 2022-03-08 15:58:08 +01:00 committed by markspanbroek
parent 22e8ea50e2
commit 78eaaa7812
7 changed files with 275 additions and 204 deletions

View File

@ -13,6 +13,7 @@ contract Proofs {
mapping(bytes32 => bool) private ids; mapping(bytes32 => bool) private ids;
mapping(bytes32 => uint256) private starts; mapping(bytes32 => uint256) private starts;
mapping(bytes32 => uint256) private ends; mapping(bytes32 => uint256) private ends;
mapping(bytes32 => uint256) private probabilities;
mapping(bytes32 => uint256) private markers; mapping(bytes32 => uint256) private markers;
mapping(bytes32 => uint256) private missed; mapping(bytes32 => uint256) private missed;
mapping(bytes32 => mapping(uint256 => bool)) private received; mapping(bytes32 => mapping(uint256 => bool)) private received;
@ -34,55 +35,95 @@ contract Proofs {
return missed[id]; return missed[id];
} }
function _expectProofs(bytes32 id, uint256 duration) internal { function periodOf(uint256 timestamp) private view returns (uint256) {
return timestamp / period;
}
function currentPeriod() private view returns (uint256) {
return periodOf(block.timestamp);
}
function _expectProofs(
bytes32 id,
uint256 probability,
uint256 duration
) internal {
require(!ids[id], "Proof id already in use"); require(!ids[id], "Proof id already in use");
ids[id] = true; ids[id] = true;
starts[id] = block.number; starts[id] = block.timestamp;
ends[id] = block.number + duration + 2 * timeout; ends[id] = block.timestamp + duration;
probabilities[id] = probability;
markers[id] = uint256(blockhash(block.number - 1)) % period; markers[id] = uint256(blockhash(block.number - 1)) % period;
} }
// Check whether a proof is required at the time of the block with the function _getChallenges(bytes32 id, uint256 proofperiod)
// specified block number. A proof has to be submitted within the proof internal
// timeout for it to be valid. Whether a proof is required is determined view
// randomly, but on average it is once every proof period. returns (Challenge memory challenge1, Challenge memory challenge2)
function _isProofRequired(bytes32 id, uint256 blocknumber) {
if (
proofperiod <= periodOf(starts[id]) || proofperiod >= periodOf(ends[id])
) {
bytes32 nullChallenge;
return (Challenge(false, nullChallenge), Challenge(false, nullChallenge));
}
uint256 blocknumber = block.number % 256;
uint256 periodnumber = proofperiod % 256;
uint256 idoffset = uint256(id) % 256;
uint256 pointer1 = (blocknumber + periodnumber + idoffset) % 256;
uint256 pointer2 = (blocknumber + periodnumber + idoffset + 128) % 256;
bytes32 blockhash1 = blockhash(block.number - 1 - pointer1);
bytes32 blockhash2 = blockhash(block.number - 1 - pointer2);
assert(uint256(blockhash1) != 0);
assert(uint256(blockhash2) != 0);
challenge1.challenge = keccak256(abi.encode(blockhash1));
challenge2.challenge = keccak256(abi.encode(blockhash2));
challenge1.isProofRequired =
uint256(challenge1.challenge) % probabilities[id] == 0;
challenge2.isProofRequired =
uint256(challenge2.challenge) % probabilities[id] == 0;
}
function _isProofRequired(bytes32 id, uint256 proofPeriod)
internal internal
view view
returns (bool) returns (bool)
{ {
if (blocknumber < starts[id] || blocknumber >= ends[id]) { Challenge memory challenge1;
return false; Challenge memory challenge2;
} (challenge1, challenge2) = _getChallenges(id, proofPeriod);
bytes32 hash = blockhash(blocknumber - 1); return challenge1.isProofRequired && challenge2.isProofRequired;
return hash != 0 && uint256(hash) % period == markers[id];
} }
function _isProofTimedOut(uint256 blocknumber) internal view returns (bool) { function _isProofRequired(bytes32 id) internal view returns (bool) {
return block.number >= blocknumber + timeout; return _isProofRequired(id, currentPeriod());
} }
function _submitProof( function _submitProof(bytes32 id, bool proof) internal {
bytes32 id,
uint256 blocknumber,
bool proof
) internal {
require(proof, "Invalid proof"); // TODO: replace bool by actual proof require(proof, "Invalid proof"); // TODO: replace bool by actual proof
require( require(!received[id][currentPeriod()], "Proof already submitted");
_isProofRequired(id, blocknumber), received[id][currentPeriod()] = true;
"No proof required for this block"
);
require(!_isProofTimedOut(blocknumber), "Proof not allowed after timeout");
require(!received[id][blocknumber], "Proof already submitted");
received[id][blocknumber] = true;
} }
function _markProofAsMissing(bytes32 id, uint256 blocknumber) internal { function _markProofAsMissing(bytes32 id, uint256 missedPeriod) internal {
require(_isProofTimedOut(blocknumber), "Proof has not timed out yet"); uint256 periodEnd = (missedPeriod + 1) * period;
require(!received[id][blocknumber], "Proof was submitted, not missing"); require(periodEnd < block.timestamp, "Period has not ended yet");
require(_isProofRequired(id, blocknumber), "Proof was not required"); require(block.timestamp < periodEnd + timeout, "Validation timed out");
require(!missing[id][blocknumber], "Proof already marked as missing"); require(!received[id][missedPeriod], "Proof was submitted, not missing");
missing[id][blocknumber] = true; require(_isProofRequired(id, missedPeriod), "Proof was not required");
require(!missing[id][missedPeriod], "Proof already marked as missing");
missing[id][missedPeriod] = true;
missed[id] += 1; missed[id] += 1;
} }
struct Challenge {
bool isProofRequired;
bytes32 challenge;
}
} }

View File

@ -29,11 +29,11 @@ contract Storage is Collateral, Marketplace, Proofs {
Offer storage offer = _offer(id); Offer storage offer = _offer(id);
require(msg.sender == offer.host, "Only host can call this function"); require(msg.sender == offer.host, "Only host can call this function");
Request storage request = _request(offer.requestId); Request storage request = _request(offer.requestId);
_expectProofs(id, request.duration); _expectProofs(id, request.proofProbability, request.duration);
} }
function finishContract(bytes32 id) public { function finishContract(bytes32 id) public {
require(block.number > proofEnd(id), "Contract has not ended yet"); require(block.timestamp > proofEnd(id), "Contract has not ended yet");
require(!finished[id], "Contract already finished"); require(!finished[id], "Contract already finished");
finished[id] = true; finished[id] = true;
Offer storage offer = _offer(id); Offer storage offer = _offer(id);
@ -56,28 +56,16 @@ contract Storage is Collateral, Marketplace, Proofs {
return _missed(contractId); return _missed(contractId);
} }
function isProofRequired(bytes32 contractId, uint256 blocknumber) function isProofRequired(bytes32 contractId) public view returns (bool) {
public return _isProofRequired(contractId);
view
returns (bool)
{
return _isProofRequired(contractId, blocknumber);
} }
function isProofTimedOut(uint256 blocknumber) public view returns (bool) { function submitProof(bytes32 contractId, bool proof) public {
return _isProofTimedOut(blocknumber); _submitProof(contractId, proof);
} }
function submitProof( function markProofAsMissing(bytes32 contractId, uint256 period) public {
bytes32 contractId, _markProofAsMissing(contractId, period);
uint256 blocknumber,
bool proof
) public {
_submitProof(contractId, blocknumber, proof);
}
function markProofAsMissing(bytes32 contractId, uint256 blocknumber) public {
_markProofAsMissing(contractId, blocknumber);
if (_missed(contractId) % slashMisses == 0) { if (_missed(contractId) % slashMisses == 0) {
Offer storage offer = _offer(contractId); Offer storage offer = _offer(contractId);
_slash(offer.host, slashPercentage); _slash(offer.host, slashPercentage);

View File

@ -28,27 +28,23 @@ contract TestProofs is Proofs {
return _missed(id); return _missed(id);
} }
function expectProofs(bytes32 id, uint256 _duration) public { function expectProofs(
_expectProofs(id, _duration);
}
function isProofRequired(bytes32 id, uint256 blocknumber)
public
view
returns (bool)
{
return _isProofRequired(id, blocknumber);
}
function submitProof(
bytes32 id, bytes32 id,
uint256 blocknumber, uint256 _probability,
bool proof uint256 _duration
) public { ) public {
_submitProof(id, blocknumber, proof); _expectProofs(id, _probability, _duration);
} }
function markProofAsMissing(bytes32 id, uint256 blocknumber) public { function isProofRequired(bytes32 id) public view returns (bool) {
_markProofAsMissing(id, blocknumber); return _isProofRequired(id);
}
function submitProof(bytes32 id, bool proof) public {
_submitProof(id, proof);
}
function markProofAsMissing(bytes32 id, uint256 _period) public {
_markProofAsMissing(id, _period);
} }
} }

View File

@ -1,190 +1,202 @@
const { expect } = require("chai") const { expect } = require("chai")
const { ethers } = require("hardhat") const { ethers } = require("hardhat")
const { mineBlock, minedBlockNumber } = require("./evm") const { mineBlock, minedBlockNumber } = require("./evm")
const {
snapshot,
revert,
currentTime,
advanceTime,
advanceTimeTo,
} = require("./evm")
describe("Proofs", function () { describe("Proofs", function () {
const id = ethers.utils.randomBytes(32) const id = ethers.utils.randomBytes(32)
const period = 10 const period = 10
const timeout = 5 const timeout = 5
const duration = 50 const duration = 1000
const probability = 2 // require a proof roughly once every 2² periods
let proofs let proofs
async function ensureEnoughBlockHistory() {
while ((await minedBlockNumber()) < 256) {
await mineBlock()
}
}
beforeEach(async function () { beforeEach(async function () {
await snapshot()
await ensureEnoughBlockHistory()
const Proofs = await ethers.getContractFactory("TestProofs") const Proofs = await ethers.getContractFactory("TestProofs")
proofs = await Proofs.deploy(period, timeout) proofs = await Proofs.deploy(period, timeout)
}) })
it("calculates an end time based on duration and timeout", async function () { afterEach(async function () {
await proofs.expectProofs(id, duration) await revert()
let start = await minedBlockNumber() })
let end = start + duration + 2 * timeout
expect(await proofs.end(id)).to.equal(end) 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)
}) })
it("does not allow ids to be reused", async function () { it("does not allow ids to be reused", async function () {
await proofs.expectProofs(id, duration) await proofs.expectProofs(id, probability, duration)
await expect(proofs.expectProofs(id, duration)).to.be.revertedWith( await expect(
"Proof id already in use" proofs.expectProofs(id, probability, duration)
) ).to.be.revertedWith("Proof id already in use")
}) })
it("requires on average a proof every period", async function () { it("requires proofs with an agreed upon probability", async function () {
let blocks = 600 const duration = 100_000
await proofs.expectProofs(id, probability, duration)
let amount = 0 let amount = 0
await proofs.expectProofs(id, blocks) for (let i = 0; i < 100; i++) {
for (let i = 0; i < blocks; i++) { if (await proofs.isProofRequired(id)) {
await mineBlock()
if (await proofs.isProofRequired(id, await minedBlockNumber())) {
amount += 1 amount += 1
} }
await advanceTime(period)
} }
let average = blocks / amount let expected = 100 / probability ** 2
expect(average).to.be.closeTo(period, period / 2) expect(amount).to.be.closeTo(expected, expected / 2)
}) })
it("requires no proof before start time", async function () { it("requires no proofs in the start period", async function () {
for (let i = 0; i < 4 * period; i++) { const startPeriod = Math.floor((await currentTime()) / period)
mineBlock() 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)
} }
await proofs.expectProofs(id, duration) })
let start = await minedBlockNumber()
for (let i = 1; i < 4 * period; i++) { it("requires no proofs in the end period", async function () {
expect(await proofs.isProofRequired(id, start - i)).to.be.false 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)
} }
}) })
describe("when proofs are required", async function () { describe("when proofs are required", async function () {
beforeEach(async function () { beforeEach(async function () {
await proofs.expectProofs(id, duration) await proofs.expectProofs(id, probability, duration)
}) })
async function mineUntilProofIsRequired(id) { async function waitUntilProofIsRequired(id) {
while (!(await proofs.isProofRequired(id, await minedBlockNumber()))) { while (!(await proofs.isProofRequired(id))) {
mineBlock() await advanceTime(period)
} }
} }
async function mineUntilProofTimeout() { function periodOf(timestamp) {
for (let i = 0; i < timeout; i++) { return Math.floor(timestamp / period)
mineBlock()
}
} }
async function mineUntilEnd() { function periodStart(p) {
const end = await proofs.end(id) return period * p
while ((await minedBlockNumber()) < end) {
mineBlock()
}
} }
it("requires no proof for blocks that are unavailable", async function () { function periodEnd(p) {
await mineUntilProofIsRequired(id) return periodStart(p + 1)
let blocknumber = await minedBlockNumber()
for (let i = 0; i < 256; i++) {
// only last 256 blocks available in solidity
mineBlock()
} }
expect(await proofs.isProofRequired(id, blocknumber)).to.be.false
})
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()
}
})
it("submits a correct proof", async function () { it("submits a correct proof", async function () {
await mineUntilProofIsRequired(id) await proofs.submitProof(id, true)
let blocknumber = await minedBlockNumber()
await proofs.submitProof(id, blocknumber, true)
}) })
it("fails proof submission when proof is incorrect", async function () { it("fails proof submission when proof is incorrect", async function () {
await mineUntilProofIsRequired(id) await expect(proofs.submitProof(id, false)).to.be.revertedWith(
let blocknumber = await minedBlockNumber() "Invalid proof"
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 () { it("fails proof submission when already submitted", async function () {
await mineUntilProofIsRequired(id) await advanceTimeTo(periodEnd(periodOf(await currentTime())))
let blocknumber = await minedBlockNumber() await proofs.submitProof(id, true)
await proofs.submitProof(id, blocknumber, true) await expect(proofs.submitProof(id, true)).to.be.revertedWith(
await expect( "Proof already submitted"
proofs.submitProof(id, blocknumber, true) )
).to.be.revertedWith("Proof already submitted")
}) })
it("marks a proof as missing", async function () { it("marks a proof as missing", async function () {
expect(await proofs.missed(id)).to.equal(0) expect(await proofs.missed(id)).to.equal(0)
await mineUntilProofIsRequired(id) await waitUntilProofIsRequired(id)
let blocknumber = await minedBlockNumber() let missedPeriod = periodOf(await currentTime())
await mineUntilProofTimeout() await advanceTimeTo(periodEnd(missedPeriod))
await proofs.markProofAsMissing(id, blocknumber) await proofs.markProofAsMissing(id, missedPeriod)
expect(await proofs.missed(id)).to.equal(1) expect(await proofs.missed(id)).to.equal(1)
}) })
it("does not mark a proof as missing before timeout", async function () { it("does not mark a proof as missing before period end", async function () {
await mineUntilProofIsRequired(id) await waitUntilProofIsRequired(id)
let blocknumber = await minedBlockNumber() let currentPeriod = periodOf(await currentTime())
await mineBlock()
await expect( await expect(
proofs.markProofAsMissing(id, blocknumber) proofs.markProofAsMissing(id, currentPeriod)
).to.be.revertedWith("Proof has not timed out yet") ).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)
await expect(
proofs.markProofAsMissing(id, currentPeriod)
).to.be.revertedWith("Validation timed out")
}) })
it("does not mark a submitted proof as missing", async function () { it("does not mark a submitted proof as missing", async function () {
await mineUntilProofIsRequired(id) await waitUntilProofIsRequired(id)
let blocknumber = await minedBlockNumber() let submittedPeriod = periodOf(await currentTime())
await proofs.submitProof(id, blocknumber, true) await proofs.submitProof(id, true)
await mineUntilProofTimeout() await advanceTimeTo(periodEnd(submittedPeriod))
await expect( await expect(
proofs.markProofAsMissing(id, blocknumber) proofs.markProofAsMissing(id, submittedPeriod)
).to.be.revertedWith("Proof was submitted, not missing") ).to.be.revertedWith("Proof was submitted, not missing")
}) })
it("does not mark proof as missing when not required", async function () { it("does not mark proof as missing when not required", async function () {
while (await proofs.isProofRequired(id, await minedBlockNumber())) { while (await proofs.isProofRequired(id)) {
mineBlock() await advanceTime(period)
} }
let blocknumber = await minedBlockNumber() let currentPeriod = periodOf(await currentTime())
await mineUntilProofTimeout() await advanceTimeTo(periodEnd(currentPeriod))
await expect( await expect(
proofs.markProofAsMissing(id, blocknumber) proofs.markProofAsMissing(id, currentPeriod)
).to.be.revertedWith("Proof was not required") ).to.be.revertedWith("Proof was not required")
}) })
it("does not mark proof as missing twice", async function () { it("does not mark proof as missing twice", async function () {
await mineUntilProofIsRequired(id) await waitUntilProofIsRequired(id)
let blocknumber = await minedBlockNumber() let missedPeriod = periodOf(await currentTime())
await mineUntilProofTimeout() await advanceTimeTo(periodEnd(missedPeriod))
await proofs.markProofAsMissing(id, blocknumber) await proofs.markProofAsMissing(id, missedPeriod)
await expect( await expect(
proofs.markProofAsMissing(id, blocknumber) proofs.markProofAsMissing(id, missedPeriod)
).to.be.revertedWith("Proof already marked as missing") ).to.be.revertedWith("Proof already marked as missing")
}) })
}) })

View File

@ -1,7 +1,13 @@
const { expect } = require("chai") const { expect } = require("chai")
const { ethers, deployments } = require("hardhat") const { ethers, deployments } = require("hardhat")
const { exampleRequest, exampleOffer } = require("./examples") const { exampleRequest, exampleOffer } = require("./examples")
const { mineBlock, minedBlockNumber } = require("./evm") const {
mineBlock,
minedBlockNumber,
advanceTime,
advanceTimeTo,
currentTime,
} = require("./evm")
const { requestId, offerId } = require("./ids") const { requestId, offerId } = require("./ids")
describe("Storage", function () { describe("Storage", function () {
@ -17,6 +23,12 @@ describe("Storage", function () {
storage = storage.connect(account) storage = storage.connect(account)
} }
async function ensureEnoughBlockHistory() {
while ((await minedBlockNumber()) < 256) {
await mineBlock()
}
}
beforeEach(async function () { beforeEach(async function () {
;[client, host] = await ethers.getSigners() ;[client, host] = await ethers.getSigners()
@ -48,6 +60,8 @@ describe("Storage", function () {
switchAccount(client) switchAccount(client)
await storage.selectOffer(offerId(offer)) await storage.selectOffer(offerId(offer))
id = offerId(offer) id = offerId(offer)
await ensureEnoughBlockHistory()
}) })
describe("starting the contract", function () { describe("starting the contract", function () {
@ -77,11 +91,9 @@ describe("Storage", function () {
await storage.startContract(id) await storage.startContract(id)
}) })
async function mineUntilEnd() { async function waitUntilEnd() {
const end = await storage.proofEnd(id) const end = (await storage.proofEnd(id)).toNumber()
while ((await minedBlockNumber()) < end) { await advanceTimeTo(end)
await mineBlock()
}
} }
// it("unlocks the host collateral", async function () { // it("unlocks the host collateral", async function () {
@ -91,7 +103,7 @@ describe("Storage", function () {
// }) // })
it("pays the host", async function () { it("pays the host", async function () {
await mineUntilEnd() await waitUntilEnd()
const startBalance = await token.balanceOf(host.address) const startBalance = await token.balanceOf(host.address)
await storage.finishContract(id) await storage.finishContract(id)
const endBalance = await token.balanceOf(host.address) const endBalance = await token.balanceOf(host.address)
@ -105,7 +117,7 @@ describe("Storage", function () {
}) })
it("can only be done once", async function () { it("can only be done once", async function () {
await mineUntilEnd() await waitUntilEnd()
await storage.finishContract(id) await storage.finishContract(id)
await expect(storage.finishContract(id)).to.be.revertedWith( await expect(storage.finishContract(id)).to.be.revertedWith(
"Contract already finished" "Contract already finished"
@ -114,20 +126,34 @@ describe("Storage", function () {
}) })
describe("slashing when missing proofs", function () { describe("slashing when missing proofs", function () {
beforeEach(function () { let period
beforeEach(async function () {
switchAccount(host) switchAccount(host)
period = (await storage.proofPeriod()).toNumber()
}) })
function periodOf(timestamp) {
return Math.floor(timestamp / period)
}
function periodStart(p) {
return period * p
}
function periodEnd(p) {
return periodStart(p + 1)
}
async function ensureProofIsMissing() { async function ensureProofIsMissing() {
while (!(await storage.isProofRequired(id, await minedBlockNumber()))) { let currentPeriod = periodOf(await currentTime())
mineBlock() await advanceTimeTo(periodEnd(currentPeriod))
while (!(await storage.isProofRequired(id))) {
await advanceTime(period)
} }
const blocknumber = await minedBlockNumber() let missedPeriod = periodOf(await currentTime())
const timeout = await storage.proofTimeout() await advanceTime(period)
for (let i = 0; i < timeout; i++) { await storage.markProofAsMissing(id, missedPeriod)
mineBlock()
}
await storage.markProofAsMissing(id, blocknumber)
} }
it("reduces collateral when too many proofs are missing", async function () { it("reduces collateral when too many proofs are missing", async function () {

View File

@ -32,6 +32,13 @@ async function advanceTime(seconds) {
await mineBlock() await mineBlock()
} }
async function advanceTimeTo(timestamp) {
if ((await currentTime()) !== timestamp) {
ethers.provider.send("evm_setNextBlockTimestamp", [timestamp])
await mineBlock()
}
}
module.exports = { module.exports = {
snapshot, snapshot,
revert, revert,
@ -39,4 +46,5 @@ module.exports = {
minedBlockNumber, minedBlockNumber,
currentTime, currentTime,
advanceTime, advanceTime,
advanceTimeTo,
} }

View File

@ -4,10 +4,10 @@ const { sha256, hexlify, randomBytes } = ethers.utils
const exampleRequest = () => ({ const exampleRequest = () => ({
client: hexlify(randomBytes(20)), client: hexlify(randomBytes(20)),
duration: 150, // 150 blocks ≈ half an hour duration: hours(10),
size: 1 * 1024 * 1024 * 1024, // 1 Gigabyte size: 1 * 1024 * 1024 * 1024, // 1 Gigabyte
contentHash: sha256("0xdeadbeef"), contentHash: sha256("0xdeadbeef"),
proofProbability: 5, // require a proof roughly once every 5^2 periods proofProbability: 2, // require a proof roughly once every 2² periods
maxPrice: 84, maxPrice: 84,
expiry: now() + hours(1), expiry: now() + hours(1),
nonce: hexlify(randomBytes(32)), nonce: hexlify(randomBytes(32)),