Format using prettier
This commit is contained in:
parent
41fd33ac7a
commit
78755ecaa2
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"overrides": [
|
||||
{
|
||||
"files": "*.js",
|
||||
"options": {
|
||||
"semi": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": "*.sol",
|
||||
"options": {
|
||||
"tabWidth": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 () {
|
||||
|
|
|
@ -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")
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue