Format using prettier

This commit is contained in:
Mark Spanbroek 2022-02-09 14:17:23 +01:00 committed by markspanbroek
parent 41fd33ac7a
commit 78755ecaa2
19 changed files with 462 additions and 358 deletions

16
.prettierrc Normal file
View File

@ -0,0 +1,16 @@
{
"overrides": [
{
"files": "*.js",
"options": {
"semi": false
}
},
{
"files": "*.sol",
"options": {
"tabWidth": 2
}
}
]
}

View File

@ -4,21 +4,20 @@ pragma solidity ^0.8.0;
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
contract Contracts {
mapping(bytes32 => bool) private ids; // contract id, equal to hash of bid
mapping(bytes32 => uint256) private durations; // contract duration in blocks
mapping(bytes32 => uint256) private sizes; // storage size in bytes
mapping(bytes32 => bytes32) private contentHashes; // hash of data to be stored
mapping(bytes32 => uint256) private proofPeriods; // period between proofs
mapping(bytes32 => uint256) private proofTimeouts; // timeout for proof submission
mapping(bytes32 => uint256) private prices; // price in coins
mapping(bytes32 => address) private hosts; // host that provides storage
mapping(bytes32=>bool) private ids; // contract id, equal to hash of bid
mapping(bytes32=>uint) private durations; // contract duration in blocks
mapping(bytes32=>uint) private sizes; // storage size in bytes
mapping(bytes32=>bytes32) private contentHashes; // hash of data to be stored
mapping(bytes32=>uint) private proofPeriods; // period between proofs
mapping(bytes32=>uint) private proofTimeouts; // timeout for proof submission
mapping(bytes32=>uint) private prices; // price in coins
mapping(bytes32=>address) private hosts; // host that provides storage
function _duration(bytes32 id) internal view returns (uint) {
function _duration(bytes32 id) internal view returns (uint256) {
return durations[id];
}
function _size(bytes32 id) internal view returns (uint) {
function _size(bytes32 id) internal view returns (uint256) {
return sizes[id];
}
@ -26,15 +25,15 @@ contract Contracts {
return contentHashes[id];
}
function _proofPeriod(bytes32 id) internal view returns (uint) {
function _proofPeriod(bytes32 id) internal view returns (uint256) {
return proofPeriods[id];
}
function _proofTimeout(bytes32 id) internal view returns (uint) {
function _proofTimeout(bytes32 id) internal view returns (uint256) {
return proofTimeouts[id];
}
function _price(bytes32 id) internal view returns (uint) {
function _price(bytes32 id) internal view returns (uint256) {
return prices[id];
}
@ -43,21 +42,18 @@ contract Contracts {
}
function _newContract(
uint duration,
uint size,
uint256 duration,
uint256 size,
bytes32 contentHash,
uint proofPeriod,
uint proofTimeout,
uint256 proofPeriod,
uint256 proofTimeout,
bytes32 nonce,
uint price,
uint256 price,
address host,
uint bidExpiry,
uint256 bidExpiry,
bytes memory requestSignature,
bytes memory bidSignature
)
internal
returns (bytes32 id)
{
) internal returns (bytes32 id) {
bytes32 requestHash = _hashRequest(
duration,
size,
@ -84,57 +80,52 @@ contract Contracts {
// Creates hash for a storage request that can be used to check its signature.
function _hashRequest(
uint duration,
uint size,
uint256 duration,
uint256 size,
bytes32 hash,
uint proofPeriod,
uint proofTimeout,
uint256 proofPeriod,
uint256 proofTimeout,
bytes32 nonce
)
private pure
returns (bytes32)
{
return keccak256(abi.encode(
"[dagger.request.v1]",
duration,
size,
hash,
proofPeriod,
proofTimeout,
nonce
));
) private pure returns (bytes32) {
return
keccak256(
abi.encode(
"[dagger.request.v1]",
duration,
size,
hash,
proofPeriod,
proofTimeout,
nonce
)
);
}
// Creates hash for a storage bid that can be used to check its signature.
function _hashBid(bytes32 requestHash, uint expiry, uint price)
private pure
returns (bytes32)
{
return keccak256(abi.encode(
"[dagger.bid.v1]",
requestHash,
expiry,
price
));
function _hashBid(
bytes32 requestHash,
uint256 expiry,
uint256 price
) private pure returns (bytes32) {
return keccak256(abi.encode("[dagger.bid.v1]", requestHash, expiry, price));
}
// Checks a signature for a storage request or bid, given its hash.
function _checkSignature(bytes memory signature, bytes32 hash, address signer)
private pure
{
function _checkSignature(
bytes memory signature,
bytes32 hash,
address signer
) private pure {
bytes32 messageHash = ECDSA.toEthSignedMessageHash(hash);
address recovered = ECDSA.recover(messageHash, signature);
require(recovered == signer, "Invalid signature");
}
function _checkBidExpiry(uint expiry) private view {
function _checkBidExpiry(uint256 expiry) private view {
require(expiry > block.timestamp, "Bid expired");
}
function _checkId(bytes32 id) private view {
require(
!ids[id],
"A contract with this id already exists"
);
require(!ids[id], "A contract with this id already exists");
}
}

View File

@ -2,45 +2,44 @@
pragma solidity ^0.8.0;
contract Proofs {
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;
mapping(bytes32 => uint256) private missed;
mapping(bytes32 => mapping(uint256 => bool)) private received;
mapping(bytes32 => mapping(uint256 => bool)) private missing;
mapping(bytes32=>bool) private ids;
mapping(bytes32=>uint) private periods;
mapping(bytes32=>uint) private timeouts;
mapping(bytes32=>uint) private starts;
mapping(bytes32=>uint) private ends;
mapping(bytes32=>uint) private markers;
mapping(bytes32=>uint) private missed;
mapping(bytes32=>mapping(uint=>bool)) private received;
mapping(bytes32=>mapping(uint=>bool)) private missing;
function _period(bytes32 id) internal view returns (uint) {
function _period(bytes32 id) internal view returns (uint256) {
return periods[id];
}
function _timeout(bytes32 id) internal view returns (uint) {
function _timeout(bytes32 id) internal view returns (uint256) {
return timeouts[id];
}
function _end(bytes32 id) internal view returns (uint) {
function _end(bytes32 id) internal view returns (uint256) {
return ends[id];
}
function _missed(bytes32 id) internal view returns (uint) {
function _missed(bytes32 id) internal view returns (uint256) {
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(uint timeout) private pure {
function _checkTimeout(uint256 timeout) private pure {
require(timeout <= 128, "Invalid proof timeout, needs to be <= 128");
}
function _expectProofs(
bytes32 id,
uint period,
uint timeout,
uint duration
uint256 period,
uint256 timeout,
uint256 duration
) internal {
require(!ids[id], "Proof id already in use");
_checkTimeout(timeout);
@ -49,32 +48,28 @@ contract Proofs {
timeouts[id] = timeout;
starts[id] = block.number;
ends[id] = block.number + duration + 2 * timeout;
markers[id] = uint(blockhash(block.number - 1)) % period;
markers[id] = uint256(blockhash(block.number - 1)) % period;
}
// 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(
bytes32 id,
uint blocknumber
)
internal view
function _isProofRequired(bytes32 id, uint256 blocknumber)
internal
view
returns (bool)
{
if (blocknumber < starts[id] || blocknumber >= ends[id]) {
return false;
}
bytes32 hash = blockhash(blocknumber - 1);
return hash != 0 && uint(hash) % periods[id] == markers[id];
return hash != 0 && uint256(hash) % periods[id] == markers[id];
}
function _isProofTimedOut(
bytes32 id,
uint blocknumber
)
internal view
function _isProofTimedOut(bytes32 id, uint256 blocknumber)
internal
view
returns (bool)
{
return block.number >= blocknumber + timeouts[id];
@ -82,11 +77,9 @@ contract Proofs {
function _submitProof(
bytes32 id,
uint blocknumber,
uint256 blocknumber,
bool proof
)
internal
{
) internal {
require(proof, "Invalid proof"); // TODO: replace bool by actual proof
require(
_isProofRequired(id, blocknumber),
@ -100,19 +93,10 @@ contract Proofs {
received[id][blocknumber] = true;
}
function _markProofAsMissing(bytes32 id, uint blocknumber) internal {
require(
_isProofTimedOut(id, blocknumber),
"Proof has not timed out yet"
);
require(
!received[id][blocknumber],
"Proof was submitted, not missing"
);
require(
_isProofRequired(id, blocknumber),
"Proof was not required"
);
function _markProofAsMissing(bytes32 id, uint256 blocknumber) internal {
require(_isProofTimedOut(id, 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");
missing[id][blocknumber] = true;
missed[id] += 1;

View File

@ -4,10 +4,9 @@ pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract Stakes {
IERC20 private token;
mapping(address=>uint) private stakes;
mapping(address=>uint) private locks;
mapping(address => uint256) private stakes;
mapping(address => uint256) private locks;
constructor(IERC20 __token) {
token = __token;
@ -17,11 +16,11 @@ contract Stakes {
return token;
}
function _stake(address account) internal view returns (uint) {
function _stake(address account) internal view returns (uint256) {
return stakes[account];
}
function _increaseStake(uint amount) internal {
function _increaseStake(uint256 amount) internal {
token.transferFrom(msg.sender, address(this), amount);
stakes[msg.sender] += amount;
}
@ -40,7 +39,7 @@ contract Stakes {
locks[account] -= 1;
}
function _slash(address account, uint percentage) internal {
stakes[account] = stakes[account] * (100 - percentage) / 100;
function _slash(address account, uint256 percentage) internal {
stakes[account] = (stakes[account] * (100 - percentage)) / 100;
}
}

View File

@ -6,41 +6,36 @@ import "./Proofs.sol";
import "./Stakes.sol";
contract Storage is Contracts, Proofs, Stakes {
uint256 public stakeAmount;
uint256 public slashMisses;
uint256 public slashPercentage;
uint public stakeAmount;
uint public slashMisses;
uint public slashPercentage;
mapping(bytes32=>bool) private finished;
mapping(bytes32 => bool) private finished;
constructor(
IERC20 token,
uint _stakeAmount,
uint _slashMisses,
uint _slashPercentage
)
Stakes(token)
{
uint256 _stakeAmount,
uint256 _slashMisses,
uint256 _slashPercentage
) Stakes(token) {
stakeAmount = _stakeAmount;
slashMisses = _slashMisses;
slashPercentage = _slashPercentage;
}
function newContract(
uint _duration,
uint _size,
uint256 _duration,
uint256 _size,
bytes32 _contentHash,
uint _proofPeriod,
uint _proofTimeout,
uint256 _proofPeriod,
uint256 _proofTimeout,
bytes32 _nonce,
uint _price,
uint256 _price,
address _host,
uint _bidExpiry,
uint256 _bidExpiry,
bytes memory requestSignature,
bytes memory bidSignature
)
public
{
) public {
require(_stake(_host) >= stakeAmount, "Insufficient stake");
_lockStake(_host);
_token().transferFrom(msg.sender, address(this), _price);
@ -76,11 +71,11 @@ contract Storage is Contracts, Proofs, Stakes {
finished[id] = true;
}
function duration(bytes32 contractId) public view returns (uint) {
function duration(bytes32 contractId) public view returns (uint256) {
return _duration(contractId);
}
function size(bytes32 contractId) public view returns (uint) {
function size(bytes32 contractId) public view returns (uint256) {
return _size(contractId);
}
@ -88,7 +83,7 @@ contract Storage is Contracts, Proofs, Stakes {
return _contentHash(contractId);
}
function price(bytes32 contractId) public view returns (uint) {
function price(bytes32 contractId) public view returns (uint256) {
return _price(contractId);
}
@ -96,41 +91,37 @@ contract Storage is Contracts, Proofs, Stakes {
return _host(contractId);
}
function proofPeriod(bytes32 contractId) public view returns (uint) {
function proofPeriod(bytes32 contractId) public view returns (uint256) {
return _proofPeriod(contractId);
}
function proofTimeout(bytes32 contractId) public view returns (uint) {
function proofTimeout(bytes32 contractId) public view returns (uint256) {
return _proofTimeout(contractId);
}
function proofEnd(bytes32 contractId) public view returns (uint) {
function proofEnd(bytes32 contractId) public view returns (uint256) {
return _end(contractId);
}
function missingProofs(bytes32 contractId) public view returns (uint) {
function missingProofs(bytes32 contractId) public view returns (uint256) {
return _missed(contractId);
}
function stake(address account) public view returns (uint) {
function stake(address account) public view returns (uint256) {
return _stake(account);
}
function isProofRequired(
bytes32 contractId,
uint blocknumber
)
public view
function isProofRequired(bytes32 contractId, uint256 blocknumber)
public
view
returns (bool)
{
return _isProofRequired(contractId, blocknumber);
}
function isProofTimedOut(
bytes32 contractId,
uint blocknumber
)
public view
function isProofTimedOut(bytes32 contractId, uint256 blocknumber)
public
view
returns (bool)
{
return _isProofTimedOut(contractId, blocknumber);
@ -138,22 +129,20 @@ contract Storage is Contracts, Proofs, Stakes {
function submitProof(
bytes32 contractId,
uint blocknumber,
uint256 blocknumber,
bool proof
)
public
{
) public {
_submitProof(contractId, blocknumber, proof);
}
function markProofAsMissing(bytes32 contractId, uint blocknumber) public {
function markProofAsMissing(bytes32 contractId, uint256 blocknumber) public {
_markProofAsMissing(contractId, blocknumber);
if (_missed(contractId) % slashMisses == 0) {
_slash(host(contractId), slashPercentage);
}
}
function increaseStake(uint amount) public {
function increaseStake(uint256 amount) public {
_increaseStake(amount);
}

View File

@ -5,22 +5,19 @@ import "./Contracts.sol";
// exposes internal functions of Contracts for testing
contract TestContracts is Contracts {
function newContract(
uint _duration,
uint _size,
uint256 _duration,
uint256 _size,
bytes32 _contentHash,
uint _proofPeriod,
uint _proofTimeout,
uint256 _proofPeriod,
uint256 _proofTimeout,
bytes32 _nonce,
uint _price,
uint256 _price,
address _host,
uint _bidExpiry,
uint256 _bidExpiry,
bytes memory requestSignature,
bytes memory bidSignature
)
public
{
) public {
_newContract(
_duration,
_size,
@ -32,14 +29,15 @@ contract TestContracts is Contracts {
_host,
_bidExpiry,
requestSignature,
bidSignature);
bidSignature
);
}
function duration(bytes32 id) public view returns (uint) {
function duration(bytes32 id) public view returns (uint256) {
return _duration(id);
}
function size(bytes32 id) public view returns (uint) {
function size(bytes32 id) public view returns (uint256) {
return _size(id);
}
@ -47,7 +45,7 @@ contract TestContracts is Contracts {
return _contentHash(id);
}
function price(bytes32 id) public view returns (uint) {
function price(bytes32 id) public view returns (uint256) {
return _price(id);
}

View File

@ -5,37 +5,34 @@ import "./Proofs.sol";
// exposes internal functions of Proofs for testing
contract TestProofs is Proofs {
function period(bytes32 id) public view returns (uint) {
function period(bytes32 id) public view returns (uint256) {
return _period(id);
}
function timeout(bytes32 id) public view returns (uint) {
function timeout(bytes32 id) public view returns (uint256) {
return _timeout(id);
}
function end(bytes32 id) public view returns (uint) {
function end(bytes32 id) public view returns (uint256) {
return _end(id);
}
function missed(bytes32 id) public view returns (uint) {
function missed(bytes32 id) public view returns (uint256) {
return _missed(id);
}
function expectProofs(
bytes32 id,
uint _period,
uint _timeout,
uint _duration
uint256 _period,
uint256 _timeout,
uint256 _duration
) public {
_expectProofs(id, _period, _timeout, _duration);
}
function isProofRequired(
bytes32 id,
uint blocknumber
)
public view
function isProofRequired(bytes32 id, uint256 blocknumber)
public
view
returns (bool)
{
return _isProofRequired(id, blocknumber);
@ -43,15 +40,13 @@ contract TestProofs is Proofs {
function submitProof(
bytes32 id,
uint blocknumber,
uint256 blocknumber,
bool proof
)
public
{
) public {
_submitProof(id, blocknumber, proof);
}
function markProofAsMissing(bytes32 id, uint blocknumber) public {
_markProofAsMissing(id, blocknumber);
function markProofAsMissing(bytes32 id, uint256 blocknumber) public {
_markProofAsMissing(id, blocknumber);
}
}

View File

@ -5,14 +5,13 @@ import "./Stakes.sol";
// exposes internal functions of Stakes for testing
contract TestStakes is Stakes {
constructor(IERC20 token) Stakes(token) {}
function stake(address account) public view returns (uint) {
function stake(address account) public view returns (uint256) {
return _stake(account);
}
function increaseStake(uint amount) public {
function increaseStake(uint256 amount) public {
_increaseStake(amount);
}
@ -28,7 +27,7 @@ contract TestStakes is Stakes {
_unlockStake(account);
}
function slash(address account, uint percentage) public {
function slash(address account, uint256 percentage) public {
_slash(account, percentage);
}
}

View File

@ -6,7 +6,7 @@ import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract TestToken is ERC20 {
constructor() ERC20("TestToken", "TST") {}
function mint(address holder, uint amount) public {
function mint(address holder, uint256 amount) public {
_mint(holder, amount);
}
}

View File

@ -1,12 +1,12 @@
module.exports = async ({deployments, getNamedAccounts}) => {
const token = await deployments.get('TestToken')
module.exports = async ({ deployments, getNamedAccounts }) => {
const token = await deployments.get("TestToken")
const stakeAmount = 100
const slashMisses = 3
const slashPercentage = 10
const args = [token.address, stakeAmount, slashMisses, slashPercentage]
const { deployer } = await getNamedAccounts()
await deployments.deploy('Storage', { args, from: deployer })
await deployments.deploy("Storage", { args, from: deployer })
}
module.exports.tags = ['Storage']
module.exports.dependencies = ['TestToken']
module.exports.tags = ["Storage"]
module.exports.dependencies = ["TestToken"]

View File

@ -1,6 +1,6 @@
module.exports = async ({deployments, getNamedAccounts}) => {
module.exports = async ({ deployments, getNamedAccounts }) => {
const { deployer } = await getNamedAccounts()
await deployments.deploy('TestToken', { from: deployer })
await deployments.deploy("TestToken", { from: deployer })
}
module.exports.tags = ['TestToken']
module.exports.tags = ["TestToken"]

119
package-lock.json generated
View File

@ -1338,6 +1338,12 @@
"color-convert": "^1.9.0"
}
},
"antlr4ts": {
"version": "0.5.0-alpha.4",
"resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz",
"integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==",
"dev": true
},
"anymatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
@ -12982,11 +12988,112 @@
"dev": true
},
"prettier": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz",
"integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==",
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz",
"integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==",
"dev": true
},
"prettier-plugin-solidity": {
"version": "1.0.0-beta.19",
"resolved": "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.0.0-beta.19.tgz",
"integrity": "sha512-xxRQ5ZiiZyUoMFLE9h7HnUDXI/daf1tnmL1msEdcKmyh7ZGQ4YklkYLC71bfBpYU2WruTb5/SFLUaEb3RApg5g==",
"dev": true,
"requires": {
"@solidity-parser/parser": "^0.14.0",
"emoji-regex": "^10.0.0",
"escape-string-regexp": "^4.0.0",
"semver": "^7.3.5",
"solidity-comments-extractor": "^0.0.7",
"string-width": "^4.2.3"
},
"dependencies": {
"@solidity-parser/parser": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.14.0.tgz",
"integrity": "sha512-cX0JJRcmPtNUJpzD2K7FdA7qQsTOk1UZnFx2k7qAg9ZRvuaH5NBe5IEdBMXGlmf2+FmjhqbygJ26H8l2SV7aKQ==",
"dev": true,
"requires": {
"antlr4ts": "^0.5.0-alpha.4"
}
},
"ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true
},
"emoji-regex": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.0.0.tgz",
"integrity": "sha512-KmJa8l6uHi1HrBI34udwlzZY1jOEuID/ft4d8BSSEdRyap7PwBEt910453PJa5MuGvxkLqlt4Uvhu7tttFHViw==",
"dev": true
},
"escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"dev": true
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true
},
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
"requires": {
"yallist": "^4.0.0"
}
},
"semver": {
"version": "7.3.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
"dev": true,
"requires": {
"lru-cache": "^6.0.0"
}
},
"string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"dependencies": {
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
}
}
},
"strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"requires": {
"ansi-regex": "^5.0.1"
}
},
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
}
}
},
"printj": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz",
@ -13360,6 +13467,12 @@
}
}
},
"solidity-comments-extractor": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz",
"integrity": "sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw==",
"dev": true
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",

View File

@ -3,7 +3,8 @@
"license": "MIT",
"scripts": {
"test": "hardhat test",
"start": "hardhat node --export 'deployment-localhost.json'"
"start": "hardhat node --export 'deployment-localhost.json'",
"format": "prettier --write contracts/**/*.sol test/**/*.js"
},
"devDependencies": {
"@nomiclabs/hardhat-ethers": "^2.0.2",
@ -14,6 +15,8 @@
"ethers": "^5.4.7",
"hardhat": "^2.6.5",
"hardhat-deploy": "^0.9.8",
"hardhat-deploy-ethers": "^0.3.0-beta.11"
"hardhat-deploy-ethers": "^0.3.0-beta.11",
"prettier": "^2.5.1",
"prettier-plugin-solidity": "^1.0.0-beta.19"
}
}

View File

@ -4,7 +4,6 @@ const { hashRequest, hashBid, sign } = require("./marketplace")
const { exampleRequest, exampleBid } = require("./examples")
describe("Contracts", function () {
const request = exampleRequest()
const bid = exampleBid()
@ -14,11 +13,11 @@ describe("Contracts", function () {
let id
beforeEach(async function () {
[client, host] = await ethers.getSigners()
;[client, host] = await ethers.getSigners()
let Contracts = await ethers.getContractFactory("TestContracts")
contracts = await Contracts.deploy()
requestHash = hashRequest(request)
bidHash = hashBid({...bid, requestHash})
bidHash = hashBid({ ...bid, requestHash })
id = bidHash
})
@ -57,72 +56,83 @@ describe("Contracts", function () {
await sign(client, requestHash),
await sign(host, bidHash)
)
await expect(contracts.newContract(
request.duration,
request.size,
request.contentHash,
request.proofPeriod,
request.proofTimeout,
request.nonce,
bid.price,
await host.getAddress(),
bid.bidExpiry,
await sign(client, requestHash),
await sign(host, bidHash)
)).to.be.revertedWith("A contract with this id already exists")
await expect(
contracts.newContract(
request.duration,
request.size,
request.contentHash,
request.proofPeriod,
request.proofTimeout,
request.nonce,
bid.price,
await host.getAddress(),
bid.bidExpiry,
await sign(client, requestHash),
await sign(host, bidHash)
)
).to.be.revertedWith("A contract with this id already exists")
})
it("cannot be created when client signature is invalid", async function () {
let invalidHash = hashRequest({...request, duration: request.duration + 1})
let invalidHash = hashRequest({
...request,
duration: request.duration + 1,
})
let invalidSignature = await sign(client, invalidHash)
await expect(contracts.newContract(
request.duration,
request.size,
request.contentHash,
request.proofPeriod,
request.proofTimeout,
request.nonce,
bid.price,
await host.getAddress(),
bid.bidExpiry,
invalidSignature,
await sign(host, bidHash)
)).to.be.revertedWith("Invalid signature")
await expect(
contracts.newContract(
request.duration,
request.size,
request.contentHash,
request.proofPeriod,
request.proofTimeout,
request.nonce,
bid.price,
await host.getAddress(),
bid.bidExpiry,
invalidSignature,
await sign(host, bidHash)
)
).to.be.revertedWith("Invalid signature")
})
it("cannot be created when host signature is invalid", async function () {
let invalidBid = hashBid({...bid, requestHash, price: bid.price - 1})
let invalidBid = hashBid({ ...bid, requestHash, price: bid.price - 1 })
let invalidSignature = await sign(host, invalidBid)
await expect(contracts.newContract(
request.duration,
request.size,
request.contentHash,
request.proofPeriod,
request.proofTimeout,
request.nonce,
bid.price,
await host.getAddress(),
bid.bidExpiry,
await sign(client, requestHash),
invalidSignature
)).to.be.revertedWith("Invalid signature")
await expect(
contracts.newContract(
request.duration,
request.size,
request.contentHash,
request.proofPeriod,
request.proofTimeout,
request.nonce,
bid.price,
await host.getAddress(),
bid.bidExpiry,
await sign(client, requestHash),
invalidSignature
)
).to.be.revertedWith("Invalid signature")
})
it("cannot be created when bid has expired", async function () {
let expired = Math.round(Date.now() / 1000) - 60 // 1 minute ago
let bidHash = hashBid({...bid, requestHash, bidExpiry: expired})
await expect(contracts.newContract(
request.duration,
request.size,
request.contentHash,
request.proofPeriod,
request.proofTimeout,
request.nonce,
bid.price,
await host.getAddress(),
expired,
await sign(client, requestHash),
await sign(host, bidHash),
)).to.be.revertedWith("Bid expired")
let bidHash = hashBid({ ...bid, requestHash, bidExpiry: expired })
await expect(
contracts.newContract(
request.duration,
request.size,
request.contentHash,
request.proofPeriod,
request.proofTimeout,
request.nonce,
bid.price,
await host.getAddress(),
expired,
await sign(client, requestHash),
await sign(host, bidHash)
)
).to.be.revertedWith("Bid expired")
})
})

View File

@ -1,9 +1,8 @@
const { expect } = require("chai")
const { ethers } = require("hardhat")
const { mineBlock, minedBlockNumber } = require ("./mining")
const { mineBlock, minedBlockNumber } = require("./mining")
describe("Proofs", function () {
const id = ethers.utils.randomBytes(32)
const period = 10
const timeout = 5
@ -16,20 +15,20 @@ describe("Proofs", function () {
proofs = await Proofs.deploy()
})
it("indicates that proofs are required", async function() {
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() {
it("calculates an endtime based on duration and timeout", async function () {
await proofs.expectProofs(id, period, timeout, 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() {
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)
@ -47,7 +46,7 @@ describe("Proofs", function () {
let blocks = 600
let amount = 0
await proofs.expectProofs(id, period, timeout, blocks)
for (let i=0; i<blocks; i++) {
for (let i = 0; i < blocks; i++) {
await mineBlock()
if (await proofs.isProofRequired(id, await minedBlockNumber())) {
amount += 1
@ -58,37 +57,36 @@ describe("Proofs", function () {
})
it("requires no proof before start time", async function () {
for (let i=0; i<4*period; i++) {
for (let i = 0; i < 4 * period; i++) {
mineBlock()
}
await proofs.expectProofs(id, period, timeout, duration)
let start = await minedBlockNumber()
for (let i=1; i<4*period; i++) {
expect(await proofs.isProofRequired(id, start-i)).to.be.false
for (let i = 1; i < 4 * period; i++) {
expect(await proofs.isProofRequired(id, start - i)).to.be.false
}
})
describe("when proofs are required", async function () {
beforeEach(async function () {
await proofs.expectProofs(id, period, timeout, duration)
})
async function mineUntilProofIsRequired(id) {
while (!await proofs.isProofRequired(id, await minedBlockNumber())) {
while (!(await proofs.isProofRequired(id, await minedBlockNumber()))) {
mineBlock()
}
}
async function mineUntilProofTimeout() {
for (let i=0; i<timeout; i++) {
for (let i = 0; i < timeout; i++) {
mineBlock()
}
}
async function mineUntilEnd() {
const end = await proofs.end(id)
while (await minedBlockNumber() < end) {
while ((await minedBlockNumber()) < end) {
mineBlock()
}
}
@ -96,7 +94,8 @@ describe("Proofs", function () {
it("requires no proof for blocks that are unavailable", async function () {
await mineUntilProofIsRequired(id)
let blocknumber = await minedBlockNumber()
for (let i=0; i<256; i++) { // only last 256 blocks available in solidity
for (let i = 0; i < 256; i++) {
// only last 256 blocks available in solidity
mineBlock()
}
expect(await proofs.isProofRequired(id, blocknumber)).to.be.false
@ -104,7 +103,7 @@ describe("Proofs", function () {
it("requires no proof after end time", async function () {
await mineUntilEnd()
for (let i=0; i<4*period; i++) {
for (let i = 0; i < 4 * period; i++) {
const blocknumber = await minedBlockNumber()
expect(await proofs.isProofRequired(id, blocknumber)).to.be.false
mineBlock()
@ -144,7 +143,7 @@ describe("Proofs", function () {
).to.be.revertedWith("Proof not allowed after timeout")
})
it("fails proof submission when already submitted", async function() {
it("fails proof submission when already submitted", async function () {
await mineUntilProofIsRequired(id)
let blocknumber = await minedBlockNumber()
await proofs.submitProof(id, blocknumber, true)

View File

@ -2,13 +2,12 @@ const { expect } = require("chai")
const { ethers } = require("hardhat")
describe("Stakes", function () {
var stakes
var token
var host
beforeEach(async function() {
[host] = await ethers.getSigners()
beforeEach(async function () {
;[host] = await ethers.getSigners()
const Stakes = await ethers.getContractFactory("TestStakes")
const TestToken = await ethers.getContractFactory("TestToken")
token = await TestToken.deploy()
@ -30,9 +29,9 @@ describe("Stakes", function () {
})
it("does not increase stake when token transfer fails", async function () {
await expect(
stakes.increaseStake(20)
).to.be.revertedWith("ERC20: transfer amount exceeds allowance")
await expect(stakes.increaseStake(20)).to.be.revertedWith(
"ERC20: transfer amount exceeds allowance"
)
})
it("allows withdrawal of stake", async function () {
@ -54,9 +53,9 @@ describe("Stakes", function () {
})
it("fails to unlock when already unlocked", async function () {
await expect(
stakes.unlockStake(host.address)
).to.be.revertedWith("Stake already unlocked")
await expect(stakes.unlockStake(host.address)).to.be.revertedWith(
"Stake already unlocked"
)
})
it("requires an equal amount of locks and unlocks", async function () {

View File

@ -2,10 +2,9 @@ const { expect } = require("chai")
const { ethers, deployments } = require("hardhat")
const { hashRequest, hashBid, sign } = require("./marketplace")
const { exampleRequest, exampleBid } = require("./examples")
const { mineBlock, minedBlockNumber } = require ("./mining")
const { mineBlock, minedBlockNumber } = require("./mining")
describe("Storage", function () {
const request = exampleRequest()
const bid = exampleBid()
@ -15,10 +14,10 @@ describe("Storage", function () {
let stakeAmount, slashMisses, slashPercentage
beforeEach(async function () {
[client, host] = await ethers.getSigners()
await deployments.fixture(['TestToken', 'Storage'])
token = await ethers.getContract('TestToken')
storage = await ethers.getContract('Storage')
;[client, host] = await ethers.getSigners()
await deployments.fixture(["TestToken", "Storage"])
token = await ethers.getContract("TestToken")
storage = await ethers.getContract("Storage")
await token.mint(client.address, 1000)
await token.mint(host.address, 1000)
stakeAmount = await storage.stakeAmount()
@ -27,7 +26,6 @@ describe("Storage", function () {
})
describe("creating a new storage contract", function () {
let id
beforeEach(async function () {
@ -35,7 +33,7 @@ describe("Storage", function () {
await token.connect(client).approve(storage.address, bid.price)
await storage.connect(host).increaseStake(stakeAmount)
let requestHash = hashRequest(request)
let bidHash = hashBid({...bid, requestHash})
let bidHash = hashBid({ ...bid, requestHash })
await storage.newContract(
request.duration,
request.size,
@ -60,16 +58,16 @@ describe("Storage", function () {
expect(await storage.proofTimeout(id)).to.equal(request.proofTimeout)
expect(await storage.price(id)).to.equal(bid.price)
expect(await storage.host(id)).to.equal(await host.getAddress())
})
})
it("locks up host stake", async function () {
await expect(
storage.connect(host).withdrawStake()
).to.be.revertedWith("Stake locked")
await expect(storage.connect(host).withdrawStake()).to.be.revertedWith(
"Stake locked"
)
})
describe("starting the contract", function () {
it("starts requiring storage proofs", async function (){
it("starts requiring storage proofs", async function () {
await storage.connect(host).startContract(id)
expect(await storage.proofEnd(id)).to.be.gt(0)
})
@ -87,14 +85,13 @@ describe("Storage", function () {
})
describe("finishing the contract", function () {
beforeEach(async function () {
await storage.connect(host).startContract(id)
})
async function mineUntilEnd() {
const end = await storage.proofEnd(id)
while (await minedBlockNumber() < end) {
while ((await minedBlockNumber()) < end) {
await mineBlock()
}
}
@ -114,28 +111,27 @@ describe("Storage", function () {
})
it("is only allowed when end time has passed", async function () {
await expect(
storage.finishContract(id)
).to.be.revertedWith("Contract has not ended yet")
await expect(storage.finishContract(id)).to.be.revertedWith(
"Contract has not ended yet"
)
})
it("can only be done once", async function () {
await mineUntilEnd()
await storage.finishContract(id)
await expect(
storage.finishContract(id)
).to.be.revertedWith("Contract already finished")
await expect(storage.finishContract(id)).to.be.revertedWith(
"Contract already finished"
)
})
})
describe("slashing when missing proofs", function () {
async function ensureProofIsMissing() {
while (!await storage.isProofRequired(id, await minedBlockNumber())) {
while (!(await storage.isProofRequired(id, await minedBlockNumber()))) {
mineBlock()
}
const blocknumber = await minedBlockNumber()
for (let i=0; i<request.proofTimeout; i++) {
for (let i = 0; i < request.proofTimeout; i++) {
mineBlock()
}
await storage.markProofAsMissing(id, blocknumber)
@ -143,12 +139,12 @@ describe("Storage", function () {
it("reduces stake when too many proofs are missing", async function () {
await storage.connect(host).startContract(id)
for (let i=0; i<slashMisses; i++) {
for (let i = 0; i < slashMisses; i++) {
await ensureProofIsMissing()
}
const expectedStake = stakeAmount * (100 - slashPercentage) / 100
const expectedStake = (stakeAmount * (100 - slashPercentage)) / 100
expect(await storage.stake(host.address)).to.equal(expectedStake)
})
})
})
})
@ -157,20 +153,22 @@ describe("Storage", function () {
await token.connect(client).approve(storage.address, bid.price)
await storage.connect(host).increaseStake(stakeAmount - 1)
let requestHash = hashRequest(request)
let bidHash = hashBid({...bid, requestHash})
await expect(storage.newContract(
request.duration,
request.size,
request.contentHash,
request.proofPeriod,
request.proofTimeout,
request.nonce,
bid.price,
await host.getAddress(),
bid.bidExpiry,
await sign(client, requestHash),
await sign(host, bidHash)
)).to.be.revertedWith("Insufficient stake")
let bidHash = hashBid({ ...bid, requestHash })
await expect(
storage.newContract(
request.duration,
request.size,
request.contentHash,
request.proofPeriod,
request.proofTimeout,
request.nonce,
bid.price,
await host.getAddress(),
bid.bidExpiry,
await sign(client, requestHash),
await sign(host, bidHash)
)
).to.be.revertedWith("Insufficient stake")
})
it("doesn't create contract without payment of price", async function () {
@ -178,20 +176,22 @@ describe("Storage", function () {
await token.connect(client).approve(storage.address, bid.price - 1)
await storage.connect(host).increaseStake(stakeAmount)
let requestHash = hashRequest(request)
let bidHash = hashBid({...bid, requestHash})
await expect(storage.newContract(
request.duration,
request.size,
request.contentHash,
request.proofPeriod,
request.proofTimeout,
request.nonce,
bid.price,
await host.getAddress(),
bid.bidExpiry,
await sign(client, requestHash),
await sign(host, bidHash)
)).to.be.revertedWith("ERC20: transfer amount exceeds allowance")
let bidHash = hashBid({ ...bid, requestHash })
await expect(
storage.newContract(
request.duration,
request.size,
request.contentHash,
request.proofPeriod,
request.proofTimeout,
request.nonce,
bid.price,
await host.getAddress(),
bid.bidExpiry,
await sign(client, requestHash),
await sign(host, bidHash)
)
).to.be.revertedWith("ERC20: transfer amount exceeds allowance")
})
})

View File

@ -6,12 +6,12 @@ const exampleRequest = () => ({
contentHash: ethers.utils.sha256("0xdeadbeef"),
proofPeriod: 8, // 8 blocks ≈ 2 minutes
proofTimeout: 4, // 4 blocks ≈ 1 minute
nonce: ethers.utils.randomBytes(32)
nonce: ethers.utils.randomBytes(32),
})
const exampleBid = () => ({
price: 42,
bidExpiry: Math.round(Date.now() / 1000) + 60 * 60 // 1 hour from now
bidExpiry: Math.round(Date.now() / 1000) + 60 * 60, // 1 hour from now
})
module.exports = { exampleRequest, exampleBid }

View File

@ -1,21 +1,30 @@
const { ethers } = require("hardhat")
function hashRequest({
duration, size, contentHash, proofPeriod, proofTimeout, nonce
duration,
size,
contentHash,
proofPeriod,
proofTimeout,
nonce,
}) {
const type = "[dagger.request.v1]"
return ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(
["string", "uint", "uint", "bytes32", "uint", "uint", "bytes32"],
[type, duration, size, contentHash, proofPeriod, proofTimeout, nonce]
))
return ethers.utils.keccak256(
ethers.utils.defaultAbiCoder.encode(
["string", "uint", "uint", "bytes32", "uint", "uint", "bytes32"],
[type, duration, size, contentHash, proofPeriod, proofTimeout, nonce]
)
)
}
function hashBid({requestHash, bidExpiry, price}) {
function hashBid({ requestHash, bidExpiry, price }) {
const type = "[dagger.bid.v1]"
return ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(
["string", "bytes32", "uint", "uint"],
[type, requestHash, bidExpiry, price]
))
return ethers.utils.keccak256(
ethers.utils.defaultAbiCoder.encode(
["string", "bytes32", "uint", "uint"],
[type, requestHash, bidExpiry, price]
)
)
}
async function sign(signer, hash) {