Use bid hash as contract id

Adds nonce to storage request to ensure uniqueness.
This commit is contained in:
Mark Spanbroek 2021-10-20 14:28:05 +02:00
parent 08cedae4bf
commit c3e85c675a
3 changed files with 44 additions and 34 deletions

View File

@ -20,48 +20,48 @@ contract StorageContracts {
} }
uint numberOfContracts; uint numberOfContracts;
mapping(uint => Contract) contracts; mapping(bytes32 => Contract) contracts;
function duration(uint contractId) public view returns (uint) { function duration(bytes32 contractId) public view returns (uint) {
return contracts[contractId].duration; return contracts[contractId].duration;
} }
function size(uint contractId) public view returns (uint) { function size(bytes32 contractId) public view returns (uint) {
return contracts[contractId].size; return contracts[contractId].size;
} }
function contentHash(uint contractId) public view returns (bytes32) { function contentHash(bytes32 contractId) public view returns (bytes32) {
return contracts[contractId].contentHash; return contracts[contractId].contentHash;
} }
function price(uint contractId) public view returns (uint) { function price(bytes32 contractId) public view returns (uint) {
return contracts[contractId].price; return contracts[contractId].price;
} }
function host(uint contractId) public view returns (address) { function host(bytes32 contractId) public view returns (address) {
return contracts[contractId].host; return contracts[contractId].host;
} }
function proofPeriod(uint contractId) public view returns (uint) { function proofPeriod(bytes32 contractId) public view returns (uint) {
return contracts[contractId].proofPeriod; return contracts[contractId].proofPeriod;
} }
function proofTimeout(uint contractId) public view returns (uint) { function proofTimeout(bytes32 contractId) public view returns (uint) {
return contracts[contractId].proofTimeout; return contracts[contractId].proofTimeout;
} }
function missingProofs(uint contractId) public view returns (uint) { function missingProofs(bytes32 contractId) public view returns (uint) {
return contracts[contractId].missingProofs; return contracts[contractId].missingProofs;
} }
function newContract( function newContract(
uint contractId,
uint _duration, uint _duration,
uint _size, uint _size,
bytes32 _contentHash, bytes32 _contentHash,
uint _price, uint _price,
uint _proofPeriod, uint _proofPeriod,
uint _proofTimeout, uint _proofTimeout,
bytes32 _nonce,
uint _bidExpiry, uint _bidExpiry,
address _host, address _host,
bytes memory requestSignature, bytes memory requestSignature,
@ -74,13 +74,15 @@ contract StorageContracts {
_size, _size,
_contentHash, _contentHash,
_proofPeriod, _proofPeriod,
_proofTimeout _proofTimeout,
_nonce
); );
bytes32 bidHash = hashBid(requestHash, _bidExpiry, _price); bytes32 bidHash = hashBid(requestHash, _bidExpiry, _price);
checkSignature(requestSignature, requestHash, msg.sender); checkSignature(requestSignature, requestHash, msg.sender);
checkSignature(bidSignature, bidHash, _host); checkSignature(bidSignature, bidHash, _host);
checkProofTimeout(_proofTimeout); checkProofTimeout(_proofTimeout);
checkBidExpiry(_bidExpiry); checkBidExpiry(_bidExpiry);
bytes32 contractId = bidHash;
checkId(contractId); checkId(contractId);
Contract storage c = contracts[contractId]; Contract storage c = contracts[contractId];
c.initialized = true; c.initialized = true;
@ -100,7 +102,8 @@ contract StorageContracts {
uint _size, uint _size,
bytes32 _hash, bytes32 _hash,
uint _proofPeriod, uint _proofPeriod,
uint _proofTimeout uint _proofTimeout,
bytes32 _nonce
) )
internal pure internal pure
returns (bytes32) returns (bytes32)
@ -111,7 +114,8 @@ contract StorageContracts {
_size, _size,
_hash, _hash,
_proofPeriod, _proofPeriod,
_proofTimeout _proofTimeout,
_nonce
)); ));
} }
@ -148,7 +152,7 @@ contract StorageContracts {
require(expiry > block.timestamp, "Bid expired"); require(expiry > block.timestamp, "Bid expired");
} }
function checkId(uint contractId) internal view { function checkId(bytes32 contractId) internal view {
require( require(
!contracts[contractId].initialized, !contracts[contractId].initialized,
"A contract with this id already exists" "A contract with this id already exists"
@ -160,7 +164,7 @@ contract StorageContracts {
// timeout for it to be valid. Whether a proof is required is determined // timeout for it to be valid. Whether a proof is required is determined
// randomly, but on average it is once every proof period. // randomly, but on average it is once every proof period.
function isProofRequired( function isProofRequired(
uint contractId, bytes32 contractId,
uint blocknumber uint blocknumber
) )
public view public view
@ -172,7 +176,7 @@ contract StorageContracts {
} }
function isProofTimedOut( function isProofTimedOut(
uint contractId, bytes32 contractId,
uint blocknumber uint blocknumber
) )
internal view internal view
@ -183,7 +187,7 @@ contract StorageContracts {
} }
function submitProof( function submitProof(
uint contractId, bytes32 contractId,
uint blocknumber, uint blocknumber,
bool proof bool proof
) )
@ -203,7 +207,7 @@ contract StorageContracts {
c.proofReceived[blocknumber] = true; c.proofReceived[blocknumber] = true;
} }
function markProofAsMissing(uint contractId, uint blocknumber) public { function markProofAsMissing(bytes32 contractId, uint blocknumber) public {
Contract storage c = contracts[contractId]; Contract storage c = contracts[contractId];
require( require(
isProofTimedOut(contractId, blocknumber), isProofTimedOut(contractId, blocknumber),

View File

@ -10,6 +10,7 @@ describe("Storage Contracts", function () {
const proofPeriod = 8 // 8 blocks ≈ 2 minutes const proofPeriod = 8 // 8 blocks ≈ 2 minutes
const proofTimeout = 4 // 4 blocks ≈ 1 minute const proofTimeout = 4 // 4 blocks ≈ 1 minute
const price = 42 const price = 42
const nonce = ethers.utils.randomBytes(32)
var contracts var contracts
var client, host var client, host
@ -26,24 +27,25 @@ describe("Storage Contracts", function () {
size, size,
contentHash, contentHash,
proofPeriod, proofPeriod,
proofTimeout proofTimeout,
nonce
) )
bidExpiry = Math.round(Date.now() / 1000) + 60 * 60 // 1 hour from now bidExpiry = Math.round(Date.now() / 1000) + 60 * 60 // 1 hour from now
bidHash = hashBid(requestHash, bidExpiry, price) bidHash = hashBid(requestHash, bidExpiry, price)
id = Math.round(Math.random() * 99999999) // randomly chosen contract id id = bidHash
}) })
describe("when properly instantiated", function () { describe("when properly instantiated", function () {
beforeEach(async function () { beforeEach(async function () {
await contracts.newContract( await contracts.newContract(
id,
duration, duration,
size, size,
contentHash, contentHash,
price, price,
proofPeriod, proofPeriod,
proofTimeout, proofTimeout,
nonce,
bidExpiry, bidExpiry,
await host.getAddress(), await host.getAddress(),
await sign(client, requestHash), await sign(client, requestHash),
@ -82,26 +84,26 @@ describe("Storage Contracts", function () {
it("cannot be created when contract id already used", async function () { it("cannot be created when contract id already used", async function () {
await contracts.newContract( await contracts.newContract(
id,
duration, duration,
size, size,
contentHash, contentHash,
price, price,
proofPeriod, proofPeriod,
proofTimeout, proofTimeout,
nonce,
bidExpiry, bidExpiry,
await host.getAddress(), await host.getAddress(),
await sign(client, requestHash), await sign(client, requestHash),
await sign(host, bidHash) await sign(host, bidHash)
) )
await expect(contracts.newContract( await expect(contracts.newContract(
id,
duration, duration,
size, size,
contentHash, contentHash,
price, price,
proofPeriod, proofPeriod,
proofTimeout, proofTimeout,
nonce,
bidExpiry, bidExpiry,
await host.getAddress(), await host.getAddress(),
await sign(client, requestHash), await sign(client, requestHash),
@ -115,17 +117,18 @@ describe("Storage Contracts", function () {
size, size,
contentHash, contentHash,
proofPeriod, proofPeriod,
proofTimeout proofTimeout,
nonce
) )
let invalidSignature = await sign(client, invalidHash) let invalidSignature = await sign(client, invalidHash)
await expect(contracts.newContract( await expect(contracts.newContract(
id,
duration, duration,
size, size,
contentHash, contentHash,
price, price,
proofPeriod, proofPeriod,
proofTimeout, proofTimeout,
nonce,
bidExpiry, bidExpiry,
await host.getAddress(), await host.getAddress(),
invalidSignature, invalidSignature,
@ -137,13 +140,13 @@ describe("Storage Contracts", function () {
let invalidBid = hashBid(requestHash, bidExpiry, price - 1) let invalidBid = hashBid(requestHash, bidExpiry, price - 1)
let invalidSignature = await sign(host, invalidBid) let invalidSignature = await sign(host, invalidBid)
await expect(contracts.newContract( await expect(contracts.newContract(
id,
duration, duration,
size, size,
contentHash, contentHash,
price, price,
proofPeriod, proofPeriod,
proofTimeout, proofTimeout,
nonce,
bidExpiry, bidExpiry,
await host.getAddress(), await host.getAddress(),
await sign(client, requestHash), await sign(client, requestHash),
@ -158,17 +161,18 @@ describe("Storage Contracts", function () {
size, size,
contentHash, contentHash,
proofPeriod, proofPeriod,
invalidTimeout invalidTimeout,
nonce
) )
bidHash = hashBid(requestHash, bidExpiry, price) bidHash = hashBid(requestHash, bidExpiry, price)
await expect(contracts.newContract( await expect(contracts.newContract(
id,
duration, duration,
size, size,
contentHash, contentHash,
price, price,
proofPeriod, proofPeriod,
invalidTimeout, invalidTimeout,
nonce,
bidExpiry, bidExpiry,
await host.getAddress(), await host.getAddress(),
await sign(client, requestHash), await sign(client, requestHash),
@ -180,13 +184,13 @@ describe("Storage Contracts", function () {
let expired = Math.round(Date.now() / 1000) - 60 // 1 minute ago let expired = Math.round(Date.now() / 1000) - 60 // 1 minute ago
let bidHash = hashBid(requestHash, expired, price) let bidHash = hashBid(requestHash, expired, price)
await expect(contracts.newContract( await expect(contracts.newContract(
id,
duration, duration,
size, size,
contentHash, contentHash,
price, price,
proofPeriod, proofPeriod,
proofTimeout, proofTimeout,
nonce,
expired, expired,
await host.getAddress(), await host.getAddress(),
await sign(client, requestHash), await sign(client, requestHash),
@ -218,13 +222,13 @@ describe("Storage Contracts", function () {
beforeEach(async function () { beforeEach(async function () {
await contracts.newContract( await contracts.newContract(
id,
duration, duration,
size, size,
contentHash, contentHash,
price, price,
proofPeriod, proofPeriod,
proofTimeout, proofTimeout,
nonce,
bidExpiry, bidExpiry,
await host.getAddress(), await host.getAddress(),
await sign(client, requestHash), await sign(client, requestHash),

View File

@ -1,16 +1,18 @@
const { ethers } = require("hardhat") const { ethers } = require("hardhat")
function hashRequest(duration, size, hash, proofPeriod, proofTimeout) { function hashRequest(duration, size, hash, proofPeriod, proofTimeout, nonce) {
const type = "[dagger.request.v1]"
return ethers.utils.solidityKeccak256( return ethers.utils.solidityKeccak256(
["string", "uint", "uint", "bytes32", "uint", "uint"], ["string", "uint", "uint", "bytes32", "uint", "uint", "bytes32"],
["[dagger.request.v1]", duration, size, hash, proofPeriod, proofTimeout] [type, duration, size, hash, proofPeriod, proofTimeout, nonce]
) )
} }
function hashBid(requestHash, expiry, price) { function hashBid(requestHash, expiry, price) {
const type = "[dagger.bid.v1]"
return ethers.utils.solidityKeccak256( return ethers.utils.solidityKeccak256(
["string", "bytes32", "uint", "uint"], ["string", "bytes32", "uint", "uint"],
["[dagger.bid.v1]", requestHash, expiry, price] [type, requestHash, expiry, price]
) )
} }