Proof period and proof timeout are network constants now
This commit is contained in:
parent
c181195487
commit
036a214427
|
@ -98,8 +98,6 @@ contract Marketplace is Collateral {
|
||||||
uint256 duration;
|
uint256 duration;
|
||||||
uint256 size;
|
uint256 size;
|
||||||
bytes32 contentHash;
|
bytes32 contentHash;
|
||||||
uint256 proofPeriod;
|
|
||||||
uint256 proofTimeout;
|
|
||||||
uint256 maxPrice;
|
uint256 maxPrice;
|
||||||
uint256 expiry;
|
uint256 expiry;
|
||||||
bytes32 nonce;
|
bytes32 nonce;
|
||||||
|
|
|
@ -2,9 +2,15 @@
|
||||||
pragma solidity ^0.8.0;
|
pragma solidity ^0.8.0;
|
||||||
|
|
||||||
contract Proofs {
|
contract Proofs {
|
||||||
|
uint256 private immutable period;
|
||||||
|
uint256 private immutable timeout;
|
||||||
|
|
||||||
|
constructor(uint256 __period, uint256 __timeout) {
|
||||||
|
period = __period;
|
||||||
|
timeout = __timeout;
|
||||||
|
}
|
||||||
|
|
||||||
mapping(bytes32 => bool) private ids;
|
mapping(bytes32 => bool) private ids;
|
||||||
mapping(bytes32 => uint256) private periods;
|
|
||||||
mapping(bytes32 => uint256) private timeouts;
|
|
||||||
mapping(bytes32 => uint256) private starts;
|
mapping(bytes32 => uint256) private starts;
|
||||||
mapping(bytes32 => uint256) private ends;
|
mapping(bytes32 => uint256) private ends;
|
||||||
mapping(bytes32 => uint256) private markers;
|
mapping(bytes32 => uint256) private markers;
|
||||||
|
@ -12,12 +18,12 @@ contract Proofs {
|
||||||
mapping(bytes32 => mapping(uint256 => bool)) private received;
|
mapping(bytes32 => mapping(uint256 => bool)) private received;
|
||||||
mapping(bytes32 => mapping(uint256 => bool)) private missing;
|
mapping(bytes32 => mapping(uint256 => bool)) private missing;
|
||||||
|
|
||||||
function _period(bytes32 id) internal view returns (uint256) {
|
function _period() internal view returns (uint256) {
|
||||||
return periods[id];
|
return period;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _timeout(bytes32 id) internal view returns (uint256) {
|
function _timeout() internal view returns (uint256) {
|
||||||
return timeouts[id];
|
return timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _end(bytes32 id) internal view returns (uint256) {
|
function _end(bytes32 id) internal view returns (uint256) {
|
||||||
|
@ -28,24 +34,9 @@ contract Proofs {
|
||||||
return missed[id];
|
return missed[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks that proof timeout is <= 128. Only the latest 256 blocks can be
|
function _expectProofs(bytes32 id, uint256 duration) internal {
|
||||||
// checked in a smart contract, so that leaves a period of at least 128 blocks
|
|
||||||
// after timeout for a validator to signal the absence of a proof.
|
|
||||||
function _checkTimeout(uint256 timeout) private pure {
|
|
||||||
require(timeout <= 128, "Invalid proof timeout > 128");
|
|
||||||
}
|
|
||||||
|
|
||||||
function _expectProofs(
|
|
||||||
bytes32 id,
|
|
||||||
uint256 period,
|
|
||||||
uint256 timeout,
|
|
||||||
uint256 duration
|
|
||||||
) internal {
|
|
||||||
require(!ids[id], "Proof id already in use");
|
require(!ids[id], "Proof id already in use");
|
||||||
_checkTimeout(timeout);
|
|
||||||
ids[id] = true;
|
ids[id] = true;
|
||||||
periods[id] = period;
|
|
||||||
timeouts[id] = timeout;
|
|
||||||
starts[id] = block.number;
|
starts[id] = block.number;
|
||||||
ends[id] = block.number + duration + 2 * timeout;
|
ends[id] = block.number + duration + 2 * timeout;
|
||||||
markers[id] = uint256(blockhash(block.number - 1)) % period;
|
markers[id] = uint256(blockhash(block.number - 1)) % period;
|
||||||
|
@ -64,15 +55,11 @@ contract Proofs {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bytes32 hash = blockhash(blocknumber - 1);
|
bytes32 hash = blockhash(blocknumber - 1);
|
||||||
return hash != 0 && uint256(hash) % periods[id] == markers[id];
|
return hash != 0 && uint256(hash) % period == markers[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
function _isProofTimedOut(bytes32 id, uint256 blocknumber)
|
function _isProofTimedOut(uint256 blocknumber) internal view returns (bool) {
|
||||||
internal
|
return block.number >= blocknumber + timeout;
|
||||||
view
|
|
||||||
returns (bool)
|
|
||||||
{
|
|
||||||
return block.number >= blocknumber + timeouts[id];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function _submitProof(
|
function _submitProof(
|
||||||
|
@ -85,16 +72,13 @@ contract Proofs {
|
||||||
_isProofRequired(id, blocknumber),
|
_isProofRequired(id, blocknumber),
|
||||||
"No proof required for this block"
|
"No proof required for this block"
|
||||||
);
|
);
|
||||||
require(
|
require(!_isProofTimedOut(blocknumber), "Proof not allowed after timeout");
|
||||||
!_isProofTimedOut(id, blocknumber),
|
|
||||||
"Proof not allowed after timeout"
|
|
||||||
);
|
|
||||||
require(!received[id][blocknumber], "Proof already submitted");
|
require(!received[id][blocknumber], "Proof already submitted");
|
||||||
received[id][blocknumber] = true;
|
received[id][blocknumber] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _markProofAsMissing(bytes32 id, uint256 blocknumber) internal {
|
function _markProofAsMissing(bytes32 id, uint256 blocknumber) internal {
|
||||||
require(_isProofTimedOut(id, blocknumber), "Proof has not timed out yet");
|
require(_isProofTimedOut(blocknumber), "Proof has not timed out yet");
|
||||||
require(!received[id][blocknumber], "Proof was submitted, not missing");
|
require(!received[id][blocknumber], "Proof was submitted, not missing");
|
||||||
require(_isProofRequired(id, blocknumber), "Proof was not required");
|
require(_isProofRequired(id, blocknumber), "Proof was not required");
|
||||||
require(!missing[id][blocknumber], "Proof already marked as missing");
|
require(!missing[id][blocknumber], "Proof already marked as missing");
|
||||||
|
|
|
@ -14,10 +14,12 @@ contract Storage is Collateral, Marketplace, Proofs {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
IERC20 token,
|
IERC20 token,
|
||||||
|
uint256 _proofPeriod,
|
||||||
|
uint256 _proofTimeout,
|
||||||
uint256 _collateralAmount,
|
uint256 _collateralAmount,
|
||||||
uint256 _slashMisses,
|
uint256 _slashMisses,
|
||||||
uint256 _slashPercentage
|
uint256 _slashPercentage
|
||||||
) Marketplace(token, _collateralAmount) {
|
) Marketplace(token, _collateralAmount) Proofs(_proofPeriod, _proofTimeout) {
|
||||||
collateralAmount = _collateralAmount;
|
collateralAmount = _collateralAmount;
|
||||||
slashMisses = _slashMisses;
|
slashMisses = _slashMisses;
|
||||||
slashPercentage = _slashPercentage;
|
slashPercentage = _slashPercentage;
|
||||||
|
@ -27,12 +29,7 @@ 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(
|
_expectProofs(id, request.duration);
|
||||||
id,
|
|
||||||
request.proofPeriod,
|
|
||||||
request.proofTimeout,
|
|
||||||
request.duration
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function finishContract(bytes32 id) public {
|
function finishContract(bytes32 id) public {
|
||||||
|
@ -43,6 +40,14 @@ contract Storage is Collateral, Marketplace, Proofs {
|
||||||
require(token.transfer(offer.host, offer.price), "Payment failed");
|
require(token.transfer(offer.host, offer.price), "Payment failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function proofPeriod() public view returns (uint256) {
|
||||||
|
return _period();
|
||||||
|
}
|
||||||
|
|
||||||
|
function proofTimeout() public view returns (uint256) {
|
||||||
|
return _timeout();
|
||||||
|
}
|
||||||
|
|
||||||
function proofEnd(bytes32 contractId) public view returns (uint256) {
|
function proofEnd(bytes32 contractId) public view returns (uint256) {
|
||||||
return _end(contractId);
|
return _end(contractId);
|
||||||
}
|
}
|
||||||
|
@ -59,12 +64,8 @@ contract Storage is Collateral, Marketplace, Proofs {
|
||||||
return _isProofRequired(contractId, blocknumber);
|
return _isProofRequired(contractId, blocknumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isProofTimedOut(bytes32 contractId, uint256 blocknumber)
|
function isProofTimedOut(uint256 blocknumber) public view returns (bool) {
|
||||||
public
|
return _isProofTimedOut(blocknumber);
|
||||||
view
|
|
||||||
returns (bool)
|
|
||||||
{
|
|
||||||
return _isProofTimedOut(contractId, blocknumber);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function submitProof(
|
function submitProof(
|
||||||
|
|
|
@ -5,12 +5,19 @@ import "./Proofs.sol";
|
||||||
|
|
||||||
// exposes internal functions of Proofs for testing
|
// exposes internal functions of Proofs for testing
|
||||||
contract TestProofs is Proofs {
|
contract TestProofs is Proofs {
|
||||||
function period(bytes32 id) public view returns (uint256) {
|
constructor(uint256 __period, uint256 __timeout)
|
||||||
return _period(id);
|
Proofs(__period, __timeout)
|
||||||
|
// solhint-disable-next-line no-empty-blocks
|
||||||
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function timeout(bytes32 id) public view returns (uint256) {
|
function period() public view returns (uint256) {
|
||||||
return _timeout(id);
|
return _period();
|
||||||
|
}
|
||||||
|
|
||||||
|
function timeout() public view returns (uint256) {
|
||||||
|
return _timeout();
|
||||||
}
|
}
|
||||||
|
|
||||||
function end(bytes32 id) public view returns (uint256) {
|
function end(bytes32 id) public view returns (uint256) {
|
||||||
|
@ -21,13 +28,8 @@ contract TestProofs is Proofs {
|
||||||
return _missed(id);
|
return _missed(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
function expectProofs(
|
function expectProofs(bytes32 id, uint256 _duration) public {
|
||||||
bytes32 id,
|
_expectProofs(id, _duration);
|
||||||
uint256 _period,
|
|
||||||
uint256 _timeout,
|
|
||||||
uint256 _duration
|
|
||||||
) public {
|
|
||||||
_expectProofs(id, _period, _timeout, _duration);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isProofRequired(bytes32 id, uint256 blocknumber)
|
function isProofRequired(bytes32 id, uint256 blocknumber)
|
||||||
|
|
|
@ -1,9 +1,18 @@
|
||||||
module.exports = async ({ deployments, getNamedAccounts }) => {
|
module.exports = async ({ deployments, getNamedAccounts }) => {
|
||||||
const token = await deployments.get("TestToken")
|
const token = await deployments.get("TestToken")
|
||||||
|
const proofPeriod = 10
|
||||||
|
const proofTimeout = 5
|
||||||
const collateralAmount = 100
|
const collateralAmount = 100
|
||||||
const slashMisses = 3
|
const slashMisses = 3
|
||||||
const slashPercentage = 10
|
const slashPercentage = 10
|
||||||
const args = [token.address, collateralAmount, slashMisses, slashPercentage]
|
const args = [
|
||||||
|
token.address,
|
||||||
|
proofPeriod,
|
||||||
|
proofTimeout,
|
||||||
|
collateralAmount,
|
||||||
|
slashMisses,
|
||||||
|
slashPercentage,
|
||||||
|
]
|
||||||
const { deployer } = await getNamedAccounts()
|
const { deployer } = await getNamedAccounts()
|
||||||
await deployments.deploy("Storage", { args, from: deployer })
|
await deployments.deploy("Storage", { args, from: deployer })
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,40 +12,27 @@ describe("Proofs", function () {
|
||||||
|
|
||||||
beforeEach(async function () {
|
beforeEach(async function () {
|
||||||
const Proofs = await ethers.getContractFactory("TestProofs")
|
const Proofs = await ethers.getContractFactory("TestProofs")
|
||||||
proofs = await Proofs.deploy()
|
proofs = await Proofs.deploy(period, timeout)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("indicates that proofs are required", async function () {
|
it("calculates an end time based on duration and timeout", async function () {
|
||||||
await proofs.expectProofs(id, period, timeout, duration)
|
await proofs.expectProofs(id, duration)
|
||||||
expect(await proofs.period(id)).to.equal(period)
|
|
||||||
expect(await proofs.timeout(id)).to.equal(timeout)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("calculates an endtime based on duration and timeout", async function () {
|
|
||||||
await proofs.expectProofs(id, period, timeout, duration)
|
|
||||||
let start = await minedBlockNumber()
|
let start = await minedBlockNumber()
|
||||||
let end = start + duration + 2 * timeout
|
let end = start + duration + 2 * timeout
|
||||||
expect(await proofs.end(id)).to.equal(end)
|
expect(await proofs.end(id)).to.equal(end)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("does not allow ids to be reused", async function () {
|
it("does not allow ids to be reused", async function () {
|
||||||
await proofs.expectProofs(id, period, timeout, duration)
|
await proofs.expectProofs(id, duration)
|
||||||
await expect(
|
await expect(proofs.expectProofs(id, duration)).to.be.revertedWith(
|
||||||
proofs.expectProofs(id, period, timeout, duration)
|
"Proof id already in use"
|
||||||
).to.be.revertedWith("Proof id already in use")
|
)
|
||||||
})
|
|
||||||
|
|
||||||
it("does not allow a proof timeout that is too large", async function () {
|
|
||||||
let invalidTimeout = 129 // max proof timeout is 128 blocks
|
|
||||||
await expect(
|
|
||||||
proofs.expectProofs(id, period, invalidTimeout, duration)
|
|
||||||
).to.be.revertedWith("Invalid proof timeout")
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it("requires on average a proof every period", async function () {
|
it("requires on average a proof every period", async function () {
|
||||||
let blocks = 600
|
let blocks = 600
|
||||||
let amount = 0
|
let amount = 0
|
||||||
await proofs.expectProofs(id, period, timeout, blocks)
|
await proofs.expectProofs(id, blocks)
|
||||||
for (let i = 0; i < blocks; i++) {
|
for (let i = 0; i < blocks; i++) {
|
||||||
await mineBlock()
|
await mineBlock()
|
||||||
if (await proofs.isProofRequired(id, await minedBlockNumber())) {
|
if (await proofs.isProofRequired(id, await minedBlockNumber())) {
|
||||||
|
@ -60,7 +47,7 @@ describe("Proofs", function () {
|
||||||
for (let i = 0; i < 4 * period; i++) {
|
for (let i = 0; i < 4 * period; i++) {
|
||||||
mineBlock()
|
mineBlock()
|
||||||
}
|
}
|
||||||
await proofs.expectProofs(id, period, timeout, duration)
|
await proofs.expectProofs(id, duration)
|
||||||
let start = await minedBlockNumber()
|
let start = await minedBlockNumber()
|
||||||
for (let i = 1; i < 4 * period; i++) {
|
for (let i = 1; i < 4 * period; i++) {
|
||||||
expect(await proofs.isProofRequired(id, start - i)).to.be.false
|
expect(await proofs.isProofRequired(id, start - i)).to.be.false
|
||||||
|
@ -69,7 +56,7 @@ describe("Proofs", function () {
|
||||||
|
|
||||||
describe("when proofs are required", async function () {
|
describe("when proofs are required", async function () {
|
||||||
beforeEach(async function () {
|
beforeEach(async function () {
|
||||||
await proofs.expectProofs(id, period, timeout, duration)
|
await proofs.expectProofs(id, duration)
|
||||||
})
|
})
|
||||||
|
|
||||||
async function mineUntilProofIsRequired(id) {
|
async function mineUntilProofIsRequired(id) {
|
||||||
|
|
|
@ -123,7 +123,8 @@ describe("Storage", function () {
|
||||||
mineBlock()
|
mineBlock()
|
||||||
}
|
}
|
||||||
const blocknumber = await minedBlockNumber()
|
const blocknumber = await minedBlockNumber()
|
||||||
for (let i = 0; i < request.proofTimeout; i++) {
|
const timeout = await storage.proofTimeout()
|
||||||
|
for (let i = 0; i < timeout; i++) {
|
||||||
mineBlock()
|
mineBlock()
|
||||||
}
|
}
|
||||||
await storage.markProofAsMissing(id, blocknumber)
|
await storage.markProofAsMissing(id, blocknumber)
|
||||||
|
|
|
@ -7,8 +7,6 @@ const exampleRequest = () => ({
|
||||||
duration: 150, // 150 blocks ≈ half an hour
|
duration: 150, // 150 blocks ≈ half an hour
|
||||||
size: 1 * 1024 * 1024 * 1024, // 1 Gigabyte
|
size: 1 * 1024 * 1024 * 1024, // 1 Gigabyte
|
||||||
contentHash: sha256("0xdeadbeef"),
|
contentHash: sha256("0xdeadbeef"),
|
||||||
proofPeriod: 8, // 8 blocks ≈ 2 minutes
|
|
||||||
proofTimeout: 4, // 4 blocks ≈ 1 minute
|
|
||||||
maxPrice: 84,
|
maxPrice: 84,
|
||||||
expiry: now() + hours(1),
|
expiry: now() + hours(1),
|
||||||
nonce: hexlify(randomBytes(32)),
|
nonce: hexlify(randomBytes(32)),
|
||||||
|
|
|
@ -11,8 +11,6 @@ function requestId(request) {
|
||||||
"bytes32",
|
"bytes32",
|
||||||
"uint256",
|
"uint256",
|
||||||
"uint256",
|
"uint256",
|
||||||
"uint256",
|
|
||||||
"uint256",
|
|
||||||
"bytes32",
|
"bytes32",
|
||||||
],
|
],
|
||||||
requestToArray(request)
|
requestToArray(request)
|
||||||
|
@ -35,8 +33,6 @@ function requestToArray(request) {
|
||||||
request.duration,
|
request.duration,
|
||||||
request.size,
|
request.size,
|
||||||
request.contentHash,
|
request.contentHash,
|
||||||
request.proofPeriod,
|
|
||||||
request.proofTimeout,
|
|
||||||
request.maxPrice,
|
request.maxPrice,
|
||||||
request.expiry,
|
request.expiry,
|
||||||
request.nonce,
|
request.nonce,
|
||||||
|
|
Loading…
Reference in New Issue