2021-10-12 14:59:34 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
pragma solidity ^0.8.0;
|
|
|
|
|
|
|
|
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
|
2021-11-01 11:30:35 +00:00
|
|
|
import "./Proofs.sol";
|
2021-10-12 14:59:34 +00:00
|
|
|
|
2021-11-01 11:30:35 +00:00
|
|
|
contract StorageContracts is Proofs {
|
2021-10-20 10:07:35 +00:00
|
|
|
|
|
|
|
struct Contract {
|
|
|
|
bool initialized; // always true, except for empty contracts in mapping
|
|
|
|
uint duration; // contract duration in seconds
|
|
|
|
uint size; // storage size in bytes
|
|
|
|
bytes32 contentHash; // hash of data that is to be stored
|
|
|
|
uint price; // price in coins
|
|
|
|
address host; // host that provides storage
|
|
|
|
}
|
|
|
|
|
|
|
|
uint numberOfContracts;
|
2021-10-20 12:28:05 +00:00
|
|
|
mapping(bytes32 => Contract) contracts;
|
2021-10-20 10:07:35 +00:00
|
|
|
|
2021-10-20 12:28:05 +00:00
|
|
|
function duration(bytes32 contractId) public view returns (uint) {
|
2021-10-20 10:07:35 +00:00
|
|
|
return contracts[contractId].duration;
|
|
|
|
}
|
|
|
|
|
2021-10-20 12:28:05 +00:00
|
|
|
function size(bytes32 contractId) public view returns (uint) {
|
2021-10-20 10:07:35 +00:00
|
|
|
return contracts[contractId].size;
|
|
|
|
}
|
|
|
|
|
2021-10-20 12:28:05 +00:00
|
|
|
function contentHash(bytes32 contractId) public view returns (bytes32) {
|
2021-10-20 10:07:35 +00:00
|
|
|
return contracts[contractId].contentHash;
|
|
|
|
}
|
|
|
|
|
2021-10-20 12:28:05 +00:00
|
|
|
function price(bytes32 contractId) public view returns (uint) {
|
2021-10-20 10:07:35 +00:00
|
|
|
return contracts[contractId].price;
|
|
|
|
}
|
|
|
|
|
2021-10-20 12:28:05 +00:00
|
|
|
function host(bytes32 contractId) public view returns (address) {
|
2021-10-20 10:07:35 +00:00
|
|
|
return contracts[contractId].host;
|
|
|
|
}
|
|
|
|
|
2021-10-20 12:28:05 +00:00
|
|
|
function proofPeriod(bytes32 contractId) public view returns (uint) {
|
2021-11-01 11:30:35 +00:00
|
|
|
return _period(contractId);
|
2021-10-20 10:07:35 +00:00
|
|
|
}
|
|
|
|
|
2021-10-20 12:28:05 +00:00
|
|
|
function proofTimeout(bytes32 contractId) public view returns (uint) {
|
2021-11-01 11:30:35 +00:00
|
|
|
return _timeout(contractId);
|
2021-10-20 10:07:35 +00:00
|
|
|
}
|
|
|
|
|
2021-10-20 12:28:05 +00:00
|
|
|
function missingProofs(bytes32 contractId) public view returns (uint) {
|
2021-11-01 11:30:35 +00:00
|
|
|
return _missed(contractId);
|
2021-10-20 10:07:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function newContract(
|
|
|
|
uint _duration,
|
|
|
|
uint _size,
|
|
|
|
bytes32 _contentHash,
|
|
|
|
uint _price,
|
|
|
|
uint _proofPeriod,
|
|
|
|
uint _proofTimeout,
|
2021-10-20 12:28:05 +00:00
|
|
|
bytes32 _nonce,
|
2021-10-20 10:07:35 +00:00
|
|
|
uint _bidExpiry,
|
|
|
|
address _host,
|
|
|
|
bytes memory requestSignature,
|
|
|
|
bytes memory bidSignature
|
|
|
|
)
|
|
|
|
public
|
2021-10-12 14:59:34 +00:00
|
|
|
{
|
2021-10-14 12:49:29 +00:00
|
|
|
bytes32 requestHash = hashRequest(
|
|
|
|
_duration,
|
|
|
|
_size,
|
|
|
|
_contentHash,
|
|
|
|
_proofPeriod,
|
2021-10-20 12:28:05 +00:00
|
|
|
_proofTimeout,
|
|
|
|
_nonce
|
2021-10-14 12:49:29 +00:00
|
|
|
);
|
2021-10-18 13:29:58 +00:00
|
|
|
bytes32 bidHash = hashBid(requestHash, _bidExpiry, _price);
|
2021-10-12 14:59:34 +00:00
|
|
|
checkSignature(requestSignature, requestHash, msg.sender);
|
|
|
|
checkSignature(bidSignature, bidHash, _host);
|
2021-10-18 13:29:58 +00:00
|
|
|
checkBidExpiry(_bidExpiry);
|
2021-10-20 12:28:05 +00:00
|
|
|
bytes32 contractId = bidHash;
|
2021-10-20 10:07:35 +00:00
|
|
|
checkId(contractId);
|
|
|
|
Contract storage c = contracts[contractId];
|
|
|
|
c.initialized = true;
|
|
|
|
c.duration = _duration;
|
|
|
|
c.size = _size;
|
|
|
|
c.price = _price;
|
|
|
|
c.contentHash = _contentHash;
|
|
|
|
c.host = _host;
|
2021-11-01 11:30:35 +00:00
|
|
|
_expectProofs(contractId, _proofPeriod, _proofTimeout);
|
2021-10-12 14:59:34 +00:00
|
|
|
}
|
|
|
|
|
2021-10-14 10:37:14 +00:00
|
|
|
// Creates hash for a storage request that can be used to check its signature.
|
2021-10-14 12:49:29 +00:00
|
|
|
function hashRequest(
|
|
|
|
uint _duration,
|
|
|
|
uint _size,
|
|
|
|
bytes32 _hash,
|
|
|
|
uint _proofPeriod,
|
2021-10-20 12:28:05 +00:00
|
|
|
uint _proofTimeout,
|
|
|
|
bytes32 _nonce
|
2021-10-14 12:49:29 +00:00
|
|
|
)
|
2021-10-12 14:59:34 +00:00
|
|
|
internal pure
|
|
|
|
returns (bytes32)
|
|
|
|
{
|
|
|
|
return keccak256(abi.encodePacked(
|
|
|
|
"[dagger.request.v1]",
|
|
|
|
_duration,
|
2021-10-14 12:01:28 +00:00
|
|
|
_size,
|
2021-10-14 12:49:29 +00:00
|
|
|
_hash,
|
2021-10-14 12:01:28 +00:00
|
|
|
_proofPeriod,
|
2021-10-20 12:28:05 +00:00
|
|
|
_proofTimeout,
|
|
|
|
_nonce
|
2021-10-12 14:59:34 +00:00
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2021-10-14 10:37:14 +00:00
|
|
|
// Creates hash for a storage bid that can be used to check its signature.
|
2021-10-18 13:29:58 +00:00
|
|
|
function hashBid(bytes32 requestHash, uint _expiry, uint _price)
|
2021-10-12 14:59:34 +00:00
|
|
|
internal pure
|
|
|
|
returns (bytes32)
|
|
|
|
{
|
|
|
|
return keccak256(abi.encodePacked(
|
|
|
|
"[dagger.bid.v1]",
|
|
|
|
requestHash,
|
2021-10-18 13:29:58 +00:00
|
|
|
_expiry,
|
2021-10-12 14:59:34 +00:00
|
|
|
_price
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2021-10-14 10:37:14 +00:00
|
|
|
// Checks a signature for a storage request or bid, given its hash.
|
2021-10-12 14:59:34 +00:00
|
|
|
function checkSignature(bytes memory signature, bytes32 hash, address signer)
|
|
|
|
internal pure
|
|
|
|
{
|
|
|
|
bytes32 messageHash = ECDSA.toEthSignedMessageHash(hash);
|
|
|
|
address recovered = ECDSA.recover(messageHash, signature);
|
|
|
|
require(recovered == signer, "Invalid signature");
|
|
|
|
}
|
|
|
|
|
2021-10-18 13:29:58 +00:00
|
|
|
function checkBidExpiry(uint expiry) internal view {
|
|
|
|
require(expiry > block.timestamp, "Bid expired");
|
|
|
|
}
|
|
|
|
|
2021-10-20 12:28:05 +00:00
|
|
|
function checkId(bytes32 contractId) internal view {
|
2021-10-20 10:07:35 +00:00
|
|
|
require(
|
|
|
|
!contracts[contractId].initialized,
|
|
|
|
"A contract with this id already exists"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-10-14 10:37:14 +00:00
|
|
|
// 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.
|
2021-10-20 10:07:35 +00:00
|
|
|
function isProofRequired(
|
2021-10-20 12:28:05 +00:00
|
|
|
bytes32 contractId,
|
2021-10-20 10:07:35 +00:00
|
|
|
uint blocknumber
|
|
|
|
)
|
|
|
|
public view
|
|
|
|
returns (bool)
|
|
|
|
{
|
2021-11-01 11:30:35 +00:00
|
|
|
return _isProofRequired(contractId, blocknumber);
|
2021-10-14 10:37:14 +00:00
|
|
|
}
|
2021-10-18 12:55:59 +00:00
|
|
|
|
2021-10-20 10:07:35 +00:00
|
|
|
function isProofTimedOut(
|
2021-10-20 12:28:05 +00:00
|
|
|
bytes32 contractId,
|
2021-10-20 10:07:35 +00:00
|
|
|
uint blocknumber
|
|
|
|
)
|
2021-11-01 11:30:35 +00:00
|
|
|
public view
|
2021-10-20 10:07:35 +00:00
|
|
|
returns (bool)
|
|
|
|
{
|
2021-11-01 11:30:35 +00:00
|
|
|
return _isProofTimedOut(contractId, blocknumber);
|
2021-10-19 07:37:03 +00:00
|
|
|
}
|
|
|
|
|
2021-10-20 10:07:35 +00:00
|
|
|
function submitProof(
|
2021-10-20 12:28:05 +00:00
|
|
|
bytes32 contractId,
|
2021-10-20 10:07:35 +00:00
|
|
|
uint blocknumber,
|
|
|
|
bool proof
|
|
|
|
)
|
|
|
|
public
|
|
|
|
{
|
2021-11-01 11:30:35 +00:00
|
|
|
_submitProof(contractId, blocknumber, proof);
|
2021-10-18 12:55:59 +00:00
|
|
|
}
|
2021-10-19 07:37:03 +00:00
|
|
|
|
2021-10-20 12:28:05 +00:00
|
|
|
function markProofAsMissing(bytes32 contractId, uint blocknumber) public {
|
2021-11-01 11:30:35 +00:00
|
|
|
_markProofAsMissing(contractId, blocknumber);
|
2021-10-19 07:37:03 +00:00
|
|
|
}
|
2021-10-12 14:59:34 +00:00
|
|
|
}
|