From c3e85c675a687d3102da4a7d4634264958e4d7d4 Mon Sep 17 00:00:00 2001 From: Mark Spanbroek Date: Wed, 20 Oct 2021 14:28:05 +0200 Subject: [PATCH] Use bid hash as contract id Adds nonce to storage request to ensure uniqueness. --- contracts/StorageContracts.sol | 40 +++++++++++++++++++--------------- test/StorageContracts.test.js | 28 ++++++++++++++---------- test/marketplace.js | 10 +++++---- 3 files changed, 44 insertions(+), 34 deletions(-) diff --git a/contracts/StorageContracts.sol b/contracts/StorageContracts.sol index 5fc31bc..5240b74 100644 --- a/contracts/StorageContracts.sol +++ b/contracts/StorageContracts.sol @@ -20,48 +20,48 @@ contract StorageContracts { } 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; } - function size(uint contractId) public view returns (uint) { + function size(bytes32 contractId) public view returns (uint) { return contracts[contractId].size; } - function contentHash(uint contractId) public view returns (bytes32) { + function contentHash(bytes32 contractId) public view returns (bytes32) { return contracts[contractId].contentHash; } - function price(uint contractId) public view returns (uint) { + function price(bytes32 contractId) public view returns (uint) { return contracts[contractId].price; } - function host(uint contractId) public view returns (address) { + function host(bytes32 contractId) public view returns (address) { return contracts[contractId].host; } - function proofPeriod(uint contractId) public view returns (uint) { + function proofPeriod(bytes32 contractId) public view returns (uint) { return contracts[contractId].proofPeriod; } - function proofTimeout(uint contractId) public view returns (uint) { + function proofTimeout(bytes32 contractId) public view returns (uint) { return contracts[contractId].proofTimeout; } - function missingProofs(uint contractId) public view returns (uint) { + function missingProofs(bytes32 contractId) public view returns (uint) { return contracts[contractId].missingProofs; } function newContract( - uint contractId, uint _duration, uint _size, bytes32 _contentHash, uint _price, uint _proofPeriod, uint _proofTimeout, + bytes32 _nonce, uint _bidExpiry, address _host, bytes memory requestSignature, @@ -74,13 +74,15 @@ contract StorageContracts { _size, _contentHash, _proofPeriod, - _proofTimeout + _proofTimeout, + _nonce ); bytes32 bidHash = hashBid(requestHash, _bidExpiry, _price); checkSignature(requestSignature, requestHash, msg.sender); checkSignature(bidSignature, bidHash, _host); checkProofTimeout(_proofTimeout); checkBidExpiry(_bidExpiry); + bytes32 contractId = bidHash; checkId(contractId); Contract storage c = contracts[contractId]; c.initialized = true; @@ -100,7 +102,8 @@ contract StorageContracts { uint _size, bytes32 _hash, uint _proofPeriod, - uint _proofTimeout + uint _proofTimeout, + bytes32 _nonce ) internal pure returns (bytes32) @@ -111,7 +114,8 @@ contract StorageContracts { _size, _hash, _proofPeriod, - _proofTimeout + _proofTimeout, + _nonce )); } @@ -148,7 +152,7 @@ contract StorageContracts { require(expiry > block.timestamp, "Bid expired"); } - function checkId(uint contractId) internal view { + function checkId(bytes32 contractId) internal view { require( !contracts[contractId].initialized, "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 // randomly, but on average it is once every proof period. function isProofRequired( - uint contractId, + bytes32 contractId, uint blocknumber ) public view @@ -172,7 +176,7 @@ contract StorageContracts { } function isProofTimedOut( - uint contractId, + bytes32 contractId, uint blocknumber ) internal view @@ -183,7 +187,7 @@ contract StorageContracts { } function submitProof( - uint contractId, + bytes32 contractId, uint blocknumber, bool proof ) @@ -203,7 +207,7 @@ contract StorageContracts { c.proofReceived[blocknumber] = true; } - function markProofAsMissing(uint contractId, uint blocknumber) public { + function markProofAsMissing(bytes32 contractId, uint blocknumber) public { Contract storage c = contracts[contractId]; require( isProofTimedOut(contractId, blocknumber), diff --git a/test/StorageContracts.test.js b/test/StorageContracts.test.js index 4e0e92b..1581948 100644 --- a/test/StorageContracts.test.js +++ b/test/StorageContracts.test.js @@ -10,6 +10,7 @@ describe("Storage Contracts", function () { const proofPeriod = 8 // 8 blocks ≈ 2 minutes const proofTimeout = 4 // 4 blocks ≈ 1 minute const price = 42 + const nonce = ethers.utils.randomBytes(32) var contracts var client, host @@ -26,24 +27,25 @@ describe("Storage Contracts", function () { size, contentHash, proofPeriod, - proofTimeout + proofTimeout, + nonce ) bidExpiry = Math.round(Date.now() / 1000) + 60 * 60 // 1 hour from now bidHash = hashBid(requestHash, bidExpiry, price) - id = Math.round(Math.random() * 99999999) // randomly chosen contract id + id = bidHash }) describe("when properly instantiated", function () { beforeEach(async function () { await contracts.newContract( - id, duration, size, contentHash, price, proofPeriod, proofTimeout, + nonce, bidExpiry, await host.getAddress(), await sign(client, requestHash), @@ -82,26 +84,26 @@ describe("Storage Contracts", function () { it("cannot be created when contract id already used", async function () { await contracts.newContract( - id, duration, size, contentHash, price, proofPeriod, proofTimeout, + nonce, bidExpiry, await host.getAddress(), await sign(client, requestHash), await sign(host, bidHash) ) await expect(contracts.newContract( - id, duration, size, contentHash, price, proofPeriod, proofTimeout, + nonce, bidExpiry, await host.getAddress(), await sign(client, requestHash), @@ -115,17 +117,18 @@ describe("Storage Contracts", function () { size, contentHash, proofPeriod, - proofTimeout + proofTimeout, + nonce ) let invalidSignature = await sign(client, invalidHash) await expect(contracts.newContract( - id, duration, size, contentHash, price, proofPeriod, proofTimeout, + nonce, bidExpiry, await host.getAddress(), invalidSignature, @@ -137,13 +140,13 @@ describe("Storage Contracts", function () { let invalidBid = hashBid(requestHash, bidExpiry, price - 1) let invalidSignature = await sign(host, invalidBid) await expect(contracts.newContract( - id, duration, size, contentHash, price, proofPeriod, proofTimeout, + nonce, bidExpiry, await host.getAddress(), await sign(client, requestHash), @@ -158,17 +161,18 @@ describe("Storage Contracts", function () { size, contentHash, proofPeriod, - invalidTimeout + invalidTimeout, + nonce ) bidHash = hashBid(requestHash, bidExpiry, price) await expect(contracts.newContract( - id, duration, size, contentHash, price, proofPeriod, invalidTimeout, + nonce, bidExpiry, await host.getAddress(), await sign(client, requestHash), @@ -180,13 +184,13 @@ describe("Storage Contracts", function () { let expired = Math.round(Date.now() / 1000) - 60 // 1 minute ago let bidHash = hashBid(requestHash, expired, price) await expect(contracts.newContract( - id, duration, size, contentHash, price, proofPeriod, proofTimeout, + nonce, expired, await host.getAddress(), await sign(client, requestHash), @@ -218,13 +222,13 @@ describe("Storage Contracts", function () { beforeEach(async function () { await contracts.newContract( - id, duration, size, contentHash, price, proofPeriod, proofTimeout, + nonce, bidExpiry, await host.getAddress(), await sign(client, requestHash), diff --git a/test/marketplace.js b/test/marketplace.js index 1af8452..36d3217 100644 --- a/test/marketplace.js +++ b/test/marketplace.js @@ -1,16 +1,18 @@ 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( - ["string", "uint", "uint", "bytes32", "uint", "uint"], - ["[dagger.request.v1]", duration, size, hash, proofPeriod, proofTimeout] + ["string", "uint", "uint", "bytes32", "uint", "uint", "bytes32"], + [type, duration, size, hash, proofPeriod, proofTimeout, nonce] ) } function hashBid(requestHash, expiry, price) { + const type = "[dagger.bid.v1]" return ethers.utils.solidityKeccak256( ["string", "bytes32", "uint", "uint"], - ["[dagger.bid.v1]", requestHash, expiry, price] + [type, requestHash, expiry, price] ) }