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