Require proofs on average once every proof period
This commit is contained in:
parent
550fcf4afe
commit
cbf34df013
|
@ -10,6 +10,7 @@ contract StorageContract {
|
|||
address public immutable host; // host that provides storage
|
||||
uint public immutable proofPeriod; // average time between proofs (in blocks)
|
||||
uint public immutable proofTimeout; // proof has to be submitted before this
|
||||
uint public immutable proofMarker; // indicates when a proof is required
|
||||
|
||||
constructor(uint _duration,
|
||||
uint _size,
|
||||
|
@ -24,15 +25,17 @@ contract StorageContract {
|
|||
bytes32 bidHash = hashBid(requestHash, _price);
|
||||
checkSignature(requestSignature, requestHash, msg.sender);
|
||||
checkSignature(bidSignature, bidHash, _host);
|
||||
checkProofTimeout(_proofTimeout);
|
||||
duration = _duration;
|
||||
size = _size;
|
||||
price = _price;
|
||||
host = _host;
|
||||
proofPeriod = _proofPeriod;
|
||||
proofTimeout = _proofTimeout;
|
||||
proofMarker = uint(blockhash(block.number - 1)) % _proofPeriod;
|
||||
}
|
||||
|
||||
// creates hash for a storage request that can be used to check its signature
|
||||
// Creates hash for a storage request that can be used to check its signature.
|
||||
function hashRequest(uint _duration, uint _size)
|
||||
internal pure
|
||||
returns (bytes32)
|
||||
|
@ -44,7 +47,7 @@ contract StorageContract {
|
|||
));
|
||||
}
|
||||
|
||||
// creates hash for a storage bid that can be used to check its signature
|
||||
// Creates hash for a storage bid that can be used to check its signature.
|
||||
function hashBid(bytes32 requestHash, uint _price)
|
||||
internal pure
|
||||
returns (bytes32)
|
||||
|
@ -56,7 +59,7 @@ contract StorageContract {
|
|||
));
|
||||
}
|
||||
|
||||
// checks a signature for a storage request or bid, given its hash
|
||||
// Checks a signature for a storage request or bid, given its hash.
|
||||
function checkSignature(bytes memory signature, bytes32 hash, address signer)
|
||||
internal pure
|
||||
{
|
||||
|
@ -65,4 +68,19 @@ contract StorageContract {
|
|||
require(recovered == signer, "Invalid signature");
|
||||
}
|
||||
|
||||
// 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 checkProofTimeout(uint timeout) internal pure {
|
||||
require(timeout <= 128, "Invalid proof timeout, needs to be <= 128");
|
||||
}
|
||||
|
||||
// Check whether a proof is required at the time of the block with the
|
||||
// specified block number. A proof has to be submitted within the proof
|
||||
// timeout for it to be valid. Whether a proof is required is determined
|
||||
// randomly, but on average it is once every proof period.
|
||||
function isProofRequired(uint blocknumber) public view returns (bool) {
|
||||
bytes32 hash = blockhash(blocknumber);
|
||||
return hash != 0 && uint(hash) % proofPeriod == proofMarker;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ describe("Storage Contract", function () {
|
|||
|
||||
const duration = 31 * 24 * 60 * 60 // 31 days
|
||||
const size = 1 * 1024 * 1024 * 1024 // 1 Gigabyte
|
||||
const proofPeriod = 64 // 64 blocks ≈ 15 minutes
|
||||
const proofTimeout = 42 // 42 blocks ≈ 10 minutes
|
||||
const proofPeriod = 8 // 8 blocks ≈ 2 minutes
|
||||
const proofTimeout = 4 // 4 blocks ≈ 1 minute
|
||||
const price = 42
|
||||
|
||||
var StorageContract
|
||||
|
@ -89,6 +89,73 @@ describe("Storage Contract", function () {
|
|||
invalidSignature
|
||||
)).to.be.revertedWith("Invalid signature")
|
||||
})
|
||||
|
||||
it("cannot be created when proof timeout is too large", async function () {
|
||||
let invalidTimeout = 129 // max proof timeout is 128 blocks
|
||||
await expect(StorageContract.deploy(
|
||||
duration,
|
||||
size,
|
||||
price,
|
||||
proofPeriod,
|
||||
invalidTimeout,
|
||||
await host.getAddress(),
|
||||
await sign(client, requestHash),
|
||||
await sign(host, bidHash),
|
||||
)).to.be.revertedWith("Invalid proof timeout")
|
||||
})
|
||||
|
||||
describe("proofs", function () {
|
||||
|
||||
async function mineBlock() {
|
||||
await ethers.provider.send("evm_mine")
|
||||
}
|
||||
|
||||
async function minedBlockNumber() {
|
||||
return await ethers.provider.getBlockNumber() - 1
|
||||
}
|
||||
|
||||
async function mineUntilProofIsRequired() {
|
||||
while (!await contract.isProofRequired(await minedBlockNumber())) {
|
||||
mineBlock()
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(async function () {
|
||||
contract = await StorageContract.deploy(
|
||||
duration,
|
||||
size,
|
||||
price,
|
||||
proofPeriod,
|
||||
proofTimeout,
|
||||
await host.getAddress(),
|
||||
await sign(client, requestHash),
|
||||
await sign(host, bidHash)
|
||||
)
|
||||
})
|
||||
|
||||
it("requires on average a proof every period", async function () {
|
||||
let blocks = 400
|
||||
let proofs = 0
|
||||
for (i=0; i<blocks; i++) {
|
||||
await mineBlock()
|
||||
if (await contract.isProofRequired(await minedBlockNumber())) {
|
||||
proofs += 1
|
||||
}
|
||||
}
|
||||
let average = blocks / proofs
|
||||
expect(average).to.be.closeTo(proofPeriod, proofPeriod / 2)
|
||||
})
|
||||
|
||||
it("requires no proof for blocks that are unavailable", async function () {
|
||||
await mineUntilProofIsRequired()
|
||||
let blocknumber = await minedBlockNumber()
|
||||
for (i=0; i<256; i++) { // only last 256 blocks are available in solidity
|
||||
mineBlock()
|
||||
}
|
||||
expect(await contract.isProofRequired(blocknumber)).to.be.false
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
// TDOO: add root hash of data
|
||||
|
@ -96,6 +163,7 @@ describe("Storage Contract", function () {
|
|||
// TODO: contract start and timeout
|
||||
// TODO: missed proofs
|
||||
// TODO: successfull proofs
|
||||
// TODO: only allow proofs after start of contract
|
||||
// TODO: payout
|
||||
// TODO: stake
|
||||
// TODO: request expiration
|
||||
|
|
Loading…
Reference in New Issue