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";
|
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
|
||||||
|
|
||||||
contract Contracts {
|
contract Contracts {
|
||||||
|
|
||||||
mapping(bytes32 => bool) private ids; // contract id, equal to hash of bid
|
mapping(bytes32 => bool) private ids; // contract id, equal to hash of bid
|
||||||
mapping(bytes32=>uint) private durations; // contract duration in blocks
|
mapping(bytes32 => uint256) private durations; // contract duration in blocks
|
||||||
mapping(bytes32=>uint) private sizes; // storage size in bytes
|
mapping(bytes32 => uint256) private sizes; // storage size in bytes
|
||||||
mapping(bytes32 => bytes32) private contentHashes; // hash of data to be stored
|
mapping(bytes32 => bytes32) private contentHashes; // hash of data to be stored
|
||||||
mapping(bytes32=>uint) private proofPeriods; // period between proofs
|
mapping(bytes32 => uint256) private proofPeriods; // period between proofs
|
||||||
mapping(bytes32=>uint) private proofTimeouts; // timeout for proof submission
|
mapping(bytes32 => uint256) private proofTimeouts; // timeout for proof submission
|
||||||
mapping(bytes32=>uint) private prices; // price in coins
|
mapping(bytes32 => uint256) private prices; // price in coins
|
||||||
mapping(bytes32 => address) private hosts; // host that provides storage
|
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];
|
return durations[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
function _size(bytes32 id) internal view returns (uint) {
|
function _size(bytes32 id) internal view returns (uint256) {
|
||||||
return sizes[id];
|
return sizes[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,15 +25,15 @@ contract Contracts {
|
||||||
return contentHashes[id];
|
return contentHashes[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
function _proofPeriod(bytes32 id) internal view returns (uint) {
|
function _proofPeriod(bytes32 id) internal view returns (uint256) {
|
||||||
return proofPeriods[id];
|
return proofPeriods[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
function _proofTimeout(bytes32 id) internal view returns (uint) {
|
function _proofTimeout(bytes32 id) internal view returns (uint256) {
|
||||||
return proofTimeouts[id];
|
return proofTimeouts[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
function _price(bytes32 id) internal view returns (uint) {
|
function _price(bytes32 id) internal view returns (uint256) {
|
||||||
return prices[id];
|
return prices[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,21 +42,18 @@ contract Contracts {
|
||||||
}
|
}
|
||||||
|
|
||||||
function _newContract(
|
function _newContract(
|
||||||
uint duration,
|
uint256 duration,
|
||||||
uint size,
|
uint256 size,
|
||||||
bytes32 contentHash,
|
bytes32 contentHash,
|
||||||
uint proofPeriod,
|
uint256 proofPeriod,
|
||||||
uint proofTimeout,
|
uint256 proofTimeout,
|
||||||
bytes32 nonce,
|
bytes32 nonce,
|
||||||
uint price,
|
uint256 price,
|
||||||
address host,
|
address host,
|
||||||
uint bidExpiry,
|
uint256 bidExpiry,
|
||||||
bytes memory requestSignature,
|
bytes memory requestSignature,
|
||||||
bytes memory bidSignature
|
bytes memory bidSignature
|
||||||
)
|
) internal returns (bytes32 id) {
|
||||||
internal
|
|
||||||
returns (bytes32 id)
|
|
||||||
{
|
|
||||||
bytes32 requestHash = _hashRequest(
|
bytes32 requestHash = _hashRequest(
|
||||||
duration,
|
duration,
|
||||||
size,
|
size,
|
||||||
|
@ -84,17 +80,16 @@ contract Contracts {
|
||||||
|
|
||||||
// Creates hash for a storage request that can be used to check its signature.
|
// Creates hash for a storage request that can be used to check its signature.
|
||||||
function _hashRequest(
|
function _hashRequest(
|
||||||
uint duration,
|
uint256 duration,
|
||||||
uint size,
|
uint256 size,
|
||||||
bytes32 hash,
|
bytes32 hash,
|
||||||
uint proofPeriod,
|
uint256 proofPeriod,
|
||||||
uint proofTimeout,
|
uint256 proofTimeout,
|
||||||
bytes32 nonce
|
bytes32 nonce
|
||||||
)
|
) private pure returns (bytes32) {
|
||||||
private pure
|
return
|
||||||
returns (bytes32)
|
keccak256(
|
||||||
{
|
abi.encode(
|
||||||
return keccak256(abi.encode(
|
|
||||||
"[dagger.request.v1]",
|
"[dagger.request.v1]",
|
||||||
duration,
|
duration,
|
||||||
size,
|
size,
|
||||||
|
@ -102,39 +97,35 @@ contract Contracts {
|
||||||
proofPeriod,
|
proofPeriod,
|
||||||
proofTimeout,
|
proofTimeout,
|
||||||
nonce
|
nonce
|
||||||
));
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates hash for a storage bid that can be used to check its signature.
|
// Creates hash for a storage bid that can be used to check its signature.
|
||||||
function _hashBid(bytes32 requestHash, uint expiry, uint price)
|
function _hashBid(
|
||||||
private pure
|
bytes32 requestHash,
|
||||||
returns (bytes32)
|
uint256 expiry,
|
||||||
{
|
uint256 price
|
||||||
return keccak256(abi.encode(
|
) private pure returns (bytes32) {
|
||||||
"[dagger.bid.v1]",
|
return keccak256(abi.encode("[dagger.bid.v1]", requestHash, expiry, price));
|
||||||
requestHash,
|
|
||||||
expiry,
|
|
||||||
price
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks a signature for a storage request or bid, given its hash.
|
// Checks a signature for a storage request or bid, given its hash.
|
||||||
function _checkSignature(bytes memory signature, bytes32 hash, address signer)
|
function _checkSignature(
|
||||||
private pure
|
bytes memory signature,
|
||||||
{
|
bytes32 hash,
|
||||||
|
address signer
|
||||||
|
) private pure {
|
||||||
bytes32 messageHash = ECDSA.toEthSignedMessageHash(hash);
|
bytes32 messageHash = ECDSA.toEthSignedMessageHash(hash);
|
||||||
address recovered = ECDSA.recover(messageHash, signature);
|
address recovered = ECDSA.recover(messageHash, signature);
|
||||||
require(recovered == signer, "Invalid signature");
|
require(recovered == signer, "Invalid signature");
|
||||||
}
|
}
|
||||||
|
|
||||||
function _checkBidExpiry(uint expiry) private view {
|
function _checkBidExpiry(uint256 expiry) private view {
|
||||||
require(expiry > block.timestamp, "Bid expired");
|
require(expiry > block.timestamp, "Bid expired");
|
||||||
}
|
}
|
||||||
|
|
||||||
function _checkId(bytes32 id) private view {
|
function _checkId(bytes32 id) private view {
|
||||||
require(
|
require(!ids[id], "A contract with this id already exists");
|
||||||
!ids[id],
|
|
||||||
"A contract with this id already exists"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,45 +2,44 @@
|
||||||
pragma solidity ^0.8.0;
|
pragma solidity ^0.8.0;
|
||||||
|
|
||||||
contract Proofs {
|
contract Proofs {
|
||||||
|
|
||||||
mapping(bytes32 => bool) private ids;
|
mapping(bytes32 => bool) private ids;
|
||||||
mapping(bytes32=>uint) private periods;
|
mapping(bytes32 => uint256) private periods;
|
||||||
mapping(bytes32=>uint) private timeouts;
|
mapping(bytes32 => uint256) private timeouts;
|
||||||
mapping(bytes32=>uint) private starts;
|
mapping(bytes32 => uint256) private starts;
|
||||||
mapping(bytes32=>uint) private ends;
|
mapping(bytes32 => uint256) private ends;
|
||||||
mapping(bytes32=>uint) private markers;
|
mapping(bytes32 => uint256) private markers;
|
||||||
mapping(bytes32=>uint) private missed;
|
mapping(bytes32 => uint256) private missed;
|
||||||
mapping(bytes32=>mapping(uint=>bool)) private received;
|
mapping(bytes32 => mapping(uint256 => bool)) private received;
|
||||||
mapping(bytes32=>mapping(uint=>bool)) private missing;
|
mapping(bytes32 => mapping(uint256 => bool)) private missing;
|
||||||
|
|
||||||
function _period(bytes32 id) internal view returns (uint) {
|
function _period(bytes32 id) internal view returns (uint256) {
|
||||||
return periods[id];
|
return periods[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
function _timeout(bytes32 id) internal view returns (uint) {
|
function _timeout(bytes32 id) internal view returns (uint256) {
|
||||||
return timeouts[id];
|
return timeouts[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
function _end(bytes32 id) internal view returns (uint) {
|
function _end(bytes32 id) internal view returns (uint256) {
|
||||||
return ends[id];
|
return ends[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
function _missed(bytes32 id) internal view returns (uint) {
|
function _missed(bytes32 id) internal view returns (uint256) {
|
||||||
return missed[id];
|
return missed[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks that proof timeout is <= 128. Only the latest 256 blocks can be
|
// 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
|
// 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.
|
// 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");
|
require(timeout <= 128, "Invalid proof timeout, needs to be <= 128");
|
||||||
}
|
}
|
||||||
|
|
||||||
function _expectProofs(
|
function _expectProofs(
|
||||||
bytes32 id,
|
bytes32 id,
|
||||||
uint period,
|
uint256 period,
|
||||||
uint timeout,
|
uint256 timeout,
|
||||||
uint duration
|
uint256 duration
|
||||||
) internal {
|
) internal {
|
||||||
require(!ids[id], "Proof id already in use");
|
require(!ids[id], "Proof id already in use");
|
||||||
_checkTimeout(timeout);
|
_checkTimeout(timeout);
|
||||||
|
@ -49,32 +48,28 @@ contract Proofs {
|
||||||
timeouts[id] = timeout;
|
timeouts[id] = timeout;
|
||||||
starts[id] = block.number;
|
starts[id] = block.number;
|
||||||
ends[id] = block.number + duration + 2 * timeout;
|
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
|
// 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
|
// 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
|
// timeout for it to be valid. Whether a proof is required is determined
|
||||||
// randomly, but on average it is once every proof period.
|
// randomly, but on average it is once every proof period.
|
||||||
function _isProofRequired(
|
function _isProofRequired(bytes32 id, uint256 blocknumber)
|
||||||
bytes32 id,
|
internal
|
||||||
uint blocknumber
|
view
|
||||||
)
|
|
||||||
internal view
|
|
||||||
returns (bool)
|
returns (bool)
|
||||||
{
|
{
|
||||||
if (blocknumber < starts[id] || blocknumber >= ends[id]) {
|
if (blocknumber < starts[id] || blocknumber >= ends[id]) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bytes32 hash = blockhash(blocknumber - 1);
|
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(
|
function _isProofTimedOut(bytes32 id, uint256 blocknumber)
|
||||||
bytes32 id,
|
internal
|
||||||
uint blocknumber
|
view
|
||||||
)
|
|
||||||
internal view
|
|
||||||
returns (bool)
|
returns (bool)
|
||||||
{
|
{
|
||||||
return block.number >= blocknumber + timeouts[id];
|
return block.number >= blocknumber + timeouts[id];
|
||||||
|
@ -82,11 +77,9 @@ contract Proofs {
|
||||||
|
|
||||||
function _submitProof(
|
function _submitProof(
|
||||||
bytes32 id,
|
bytes32 id,
|
||||||
uint blocknumber,
|
uint256 blocknumber,
|
||||||
bool proof
|
bool proof
|
||||||
)
|
) internal {
|
||||||
internal
|
|
||||||
{
|
|
||||||
require(proof, "Invalid proof"); // TODO: replace bool by actual proof
|
require(proof, "Invalid proof"); // TODO: replace bool by actual proof
|
||||||
require(
|
require(
|
||||||
_isProofRequired(id, blocknumber),
|
_isProofRequired(id, blocknumber),
|
||||||
|
@ -100,19 +93,10 @@ contract Proofs {
|
||||||
received[id][blocknumber] = true;
|
received[id][blocknumber] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _markProofAsMissing(bytes32 id, uint blocknumber) internal {
|
function _markProofAsMissing(bytes32 id, uint256 blocknumber) internal {
|
||||||
require(
|
require(_isProofTimedOut(id, blocknumber), "Proof has not timed out yet");
|
||||||
_isProofTimedOut(id, blocknumber),
|
require(!received[id][blocknumber], "Proof was submitted, not missing");
|
||||||
"Proof has not timed out yet"
|
require(_isProofRequired(id, blocknumber), "Proof was not required");
|
||||||
);
|
|
||||||
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");
|
require(!missing[id][blocknumber], "Proof already marked as missing");
|
||||||
missing[id][blocknumber] = true;
|
missing[id][blocknumber] = true;
|
||||||
missed[id] += 1;
|
missed[id] += 1;
|
||||||
|
|
|
@ -4,10 +4,9 @@ pragma solidity ^0.8.0;
|
||||||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||||
|
|
||||||
contract Stakes {
|
contract Stakes {
|
||||||
|
|
||||||
IERC20 private token;
|
IERC20 private token;
|
||||||
mapping(address=>uint) private stakes;
|
mapping(address => uint256) private stakes;
|
||||||
mapping(address=>uint) private locks;
|
mapping(address => uint256) private locks;
|
||||||
|
|
||||||
constructor(IERC20 __token) {
|
constructor(IERC20 __token) {
|
||||||
token = __token;
|
token = __token;
|
||||||
|
@ -17,11 +16,11 @@ contract Stakes {
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _stake(address account) internal view returns (uint) {
|
function _stake(address account) internal view returns (uint256) {
|
||||||
return stakes[account];
|
return stakes[account];
|
||||||
}
|
}
|
||||||
|
|
||||||
function _increaseStake(uint amount) internal {
|
function _increaseStake(uint256 amount) internal {
|
||||||
token.transferFrom(msg.sender, address(this), amount);
|
token.transferFrom(msg.sender, address(this), amount);
|
||||||
stakes[msg.sender] += amount;
|
stakes[msg.sender] += amount;
|
||||||
}
|
}
|
||||||
|
@ -40,7 +39,7 @@ contract Stakes {
|
||||||
locks[account] -= 1;
|
locks[account] -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _slash(address account, uint percentage) internal {
|
function _slash(address account, uint256 percentage) internal {
|
||||||
stakes[account] = stakes[account] * (100 - percentage) / 100;
|
stakes[account] = (stakes[account] * (100 - percentage)) / 100;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,41 +6,36 @@ import "./Proofs.sol";
|
||||||
import "./Stakes.sol";
|
import "./Stakes.sol";
|
||||||
|
|
||||||
contract Storage is Contracts, Proofs, Stakes {
|
contract Storage is Contracts, Proofs, Stakes {
|
||||||
|
uint256 public stakeAmount;
|
||||||
uint public stakeAmount;
|
uint256 public slashMisses;
|
||||||
uint public slashMisses;
|
uint256 public slashPercentage;
|
||||||
uint public slashPercentage;
|
|
||||||
|
|
||||||
mapping(bytes32 => bool) private finished;
|
mapping(bytes32 => bool) private finished;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
IERC20 token,
|
IERC20 token,
|
||||||
uint _stakeAmount,
|
uint256 _stakeAmount,
|
||||||
uint _slashMisses,
|
uint256 _slashMisses,
|
||||||
uint _slashPercentage
|
uint256 _slashPercentage
|
||||||
)
|
) Stakes(token) {
|
||||||
Stakes(token)
|
|
||||||
{
|
|
||||||
stakeAmount = _stakeAmount;
|
stakeAmount = _stakeAmount;
|
||||||
slashMisses = _slashMisses;
|
slashMisses = _slashMisses;
|
||||||
slashPercentage = _slashPercentage;
|
slashPercentage = _slashPercentage;
|
||||||
}
|
}
|
||||||
|
|
||||||
function newContract(
|
function newContract(
|
||||||
uint _duration,
|
uint256 _duration,
|
||||||
uint _size,
|
uint256 _size,
|
||||||
bytes32 _contentHash,
|
bytes32 _contentHash,
|
||||||
uint _proofPeriod,
|
uint256 _proofPeriod,
|
||||||
uint _proofTimeout,
|
uint256 _proofTimeout,
|
||||||
bytes32 _nonce,
|
bytes32 _nonce,
|
||||||
uint _price,
|
uint256 _price,
|
||||||
address _host,
|
address _host,
|
||||||
uint _bidExpiry,
|
uint256 _bidExpiry,
|
||||||
bytes memory requestSignature,
|
bytes memory requestSignature,
|
||||||
bytes memory bidSignature
|
bytes memory bidSignature
|
||||||
)
|
) public {
|
||||||
public
|
|
||||||
{
|
|
||||||
require(_stake(_host) >= stakeAmount, "Insufficient stake");
|
require(_stake(_host) >= stakeAmount, "Insufficient stake");
|
||||||
_lockStake(_host);
|
_lockStake(_host);
|
||||||
_token().transferFrom(msg.sender, address(this), _price);
|
_token().transferFrom(msg.sender, address(this), _price);
|
||||||
|
@ -76,11 +71,11 @@ contract Storage is Contracts, Proofs, Stakes {
|
||||||
finished[id] = true;
|
finished[id] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function duration(bytes32 contractId) public view returns (uint) {
|
function duration(bytes32 contractId) public view returns (uint256) {
|
||||||
return _duration(contractId);
|
return _duration(contractId);
|
||||||
}
|
}
|
||||||
|
|
||||||
function size(bytes32 contractId) public view returns (uint) {
|
function size(bytes32 contractId) public view returns (uint256) {
|
||||||
return _size(contractId);
|
return _size(contractId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +83,7 @@ contract Storage is Contracts, Proofs, Stakes {
|
||||||
return _contentHash(contractId);
|
return _contentHash(contractId);
|
||||||
}
|
}
|
||||||
|
|
||||||
function price(bytes32 contractId) public view returns (uint) {
|
function price(bytes32 contractId) public view returns (uint256) {
|
||||||
return _price(contractId);
|
return _price(contractId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,41 +91,37 @@ contract Storage is Contracts, Proofs, Stakes {
|
||||||
return _host(contractId);
|
return _host(contractId);
|
||||||
}
|
}
|
||||||
|
|
||||||
function proofPeriod(bytes32 contractId) public view returns (uint) {
|
function proofPeriod(bytes32 contractId) public view returns (uint256) {
|
||||||
return _proofPeriod(contractId);
|
return _proofPeriod(contractId);
|
||||||
}
|
}
|
||||||
|
|
||||||
function proofTimeout(bytes32 contractId) public view returns (uint) {
|
function proofTimeout(bytes32 contractId) public view returns (uint256) {
|
||||||
return _proofTimeout(contractId);
|
return _proofTimeout(contractId);
|
||||||
}
|
}
|
||||||
|
|
||||||
function proofEnd(bytes32 contractId) public view returns (uint) {
|
function proofEnd(bytes32 contractId) public view returns (uint256) {
|
||||||
return _end(contractId);
|
return _end(contractId);
|
||||||
}
|
}
|
||||||
|
|
||||||
function missingProofs(bytes32 contractId) public view returns (uint) {
|
function missingProofs(bytes32 contractId) public view returns (uint256) {
|
||||||
return _missed(contractId);
|
return _missed(contractId);
|
||||||
}
|
}
|
||||||
|
|
||||||
function stake(address account) public view returns (uint) {
|
function stake(address account) public view returns (uint256) {
|
||||||
return _stake(account);
|
return _stake(account);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isProofRequired(
|
function isProofRequired(bytes32 contractId, uint256 blocknumber)
|
||||||
bytes32 contractId,
|
public
|
||||||
uint blocknumber
|
view
|
||||||
)
|
|
||||||
public view
|
|
||||||
returns (bool)
|
returns (bool)
|
||||||
{
|
{
|
||||||
return _isProofRequired(contractId, blocknumber);
|
return _isProofRequired(contractId, blocknumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isProofTimedOut(
|
function isProofTimedOut(bytes32 contractId, uint256 blocknumber)
|
||||||
bytes32 contractId,
|
public
|
||||||
uint blocknumber
|
view
|
||||||
)
|
|
||||||
public view
|
|
||||||
returns (bool)
|
returns (bool)
|
||||||
{
|
{
|
||||||
return _isProofTimedOut(contractId, blocknumber);
|
return _isProofTimedOut(contractId, blocknumber);
|
||||||
|
@ -138,22 +129,20 @@ contract Storage is Contracts, Proofs, Stakes {
|
||||||
|
|
||||||
function submitProof(
|
function submitProof(
|
||||||
bytes32 contractId,
|
bytes32 contractId,
|
||||||
uint blocknumber,
|
uint256 blocknumber,
|
||||||
bool proof
|
bool proof
|
||||||
)
|
) public {
|
||||||
public
|
|
||||||
{
|
|
||||||
_submitProof(contractId, blocknumber, proof);
|
_submitProof(contractId, blocknumber, proof);
|
||||||
}
|
}
|
||||||
|
|
||||||
function markProofAsMissing(bytes32 contractId, uint blocknumber) public {
|
function markProofAsMissing(bytes32 contractId, uint256 blocknumber) public {
|
||||||
_markProofAsMissing(contractId, blocknumber);
|
_markProofAsMissing(contractId, blocknumber);
|
||||||
if (_missed(contractId) % slashMisses == 0) {
|
if (_missed(contractId) % slashMisses == 0) {
|
||||||
_slash(host(contractId), slashPercentage);
|
_slash(host(contractId), slashPercentage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function increaseStake(uint amount) public {
|
function increaseStake(uint256 amount) public {
|
||||||
_increaseStake(amount);
|
_increaseStake(amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,22 +5,19 @@ import "./Contracts.sol";
|
||||||
|
|
||||||
// exposes internal functions of Contracts for testing
|
// exposes internal functions of Contracts for testing
|
||||||
contract TestContracts is Contracts {
|
contract TestContracts is Contracts {
|
||||||
|
|
||||||
function newContract(
|
function newContract(
|
||||||
uint _duration,
|
uint256 _duration,
|
||||||
uint _size,
|
uint256 _size,
|
||||||
bytes32 _contentHash,
|
bytes32 _contentHash,
|
||||||
uint _proofPeriod,
|
uint256 _proofPeriod,
|
||||||
uint _proofTimeout,
|
uint256 _proofTimeout,
|
||||||
bytes32 _nonce,
|
bytes32 _nonce,
|
||||||
uint _price,
|
uint256 _price,
|
||||||
address _host,
|
address _host,
|
||||||
uint _bidExpiry,
|
uint256 _bidExpiry,
|
||||||
bytes memory requestSignature,
|
bytes memory requestSignature,
|
||||||
bytes memory bidSignature
|
bytes memory bidSignature
|
||||||
)
|
) public {
|
||||||
public
|
|
||||||
{
|
|
||||||
_newContract(
|
_newContract(
|
||||||
_duration,
|
_duration,
|
||||||
_size,
|
_size,
|
||||||
|
@ -32,14 +29,15 @@ contract TestContracts is Contracts {
|
||||||
_host,
|
_host,
|
||||||
_bidExpiry,
|
_bidExpiry,
|
||||||
requestSignature,
|
requestSignature,
|
||||||
bidSignature);
|
bidSignature
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function duration(bytes32 id) public view returns (uint) {
|
function duration(bytes32 id) public view returns (uint256) {
|
||||||
return _duration(id);
|
return _duration(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
function size(bytes32 id) public view returns (uint) {
|
function size(bytes32 id) public view returns (uint256) {
|
||||||
return _size(id);
|
return _size(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +45,7 @@ contract TestContracts is Contracts {
|
||||||
return _contentHash(id);
|
return _contentHash(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
function price(bytes32 id) public view returns (uint) {
|
function price(bytes32 id) public view returns (uint256) {
|
||||||
return _price(id);
|
return _price(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,37 +5,34 @@ import "./Proofs.sol";
|
||||||
|
|
||||||
// exposes internal functions of Proofs for testing
|
// exposes internal functions of Proofs for testing
|
||||||
contract TestProofs is Proofs {
|
contract TestProofs is Proofs {
|
||||||
|
function period(bytes32 id) public view returns (uint256) {
|
||||||
function period(bytes32 id) public view returns (uint) {
|
|
||||||
return _period(id);
|
return _period(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
function timeout(bytes32 id) public view returns (uint) {
|
function timeout(bytes32 id) public view returns (uint256) {
|
||||||
return _timeout(id);
|
return _timeout(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
function end(bytes32 id) public view returns (uint) {
|
function end(bytes32 id) public view returns (uint256) {
|
||||||
return _end(id);
|
return _end(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
function missed(bytes32 id) public view returns (uint) {
|
function missed(bytes32 id) public view returns (uint256) {
|
||||||
return _missed(id);
|
return _missed(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
function expectProofs(
|
function expectProofs(
|
||||||
bytes32 id,
|
bytes32 id,
|
||||||
uint _period,
|
uint256 _period,
|
||||||
uint _timeout,
|
uint256 _timeout,
|
||||||
uint _duration
|
uint256 _duration
|
||||||
) public {
|
) public {
|
||||||
_expectProofs(id, _period, _timeout, _duration);
|
_expectProofs(id, _period, _timeout, _duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isProofRequired(
|
function isProofRequired(bytes32 id, uint256 blocknumber)
|
||||||
bytes32 id,
|
public
|
||||||
uint blocknumber
|
view
|
||||||
)
|
|
||||||
public view
|
|
||||||
returns (bool)
|
returns (bool)
|
||||||
{
|
{
|
||||||
return _isProofRequired(id, blocknumber);
|
return _isProofRequired(id, blocknumber);
|
||||||
|
@ -43,15 +40,13 @@ contract TestProofs is Proofs {
|
||||||
|
|
||||||
function submitProof(
|
function submitProof(
|
||||||
bytes32 id,
|
bytes32 id,
|
||||||
uint blocknumber,
|
uint256 blocknumber,
|
||||||
bool proof
|
bool proof
|
||||||
)
|
) public {
|
||||||
public
|
|
||||||
{
|
|
||||||
_submitProof(id, blocknumber, proof);
|
_submitProof(id, blocknumber, proof);
|
||||||
}
|
}
|
||||||
|
|
||||||
function markProofAsMissing(bytes32 id, uint blocknumber) public {
|
function markProofAsMissing(bytes32 id, uint256 blocknumber) public {
|
||||||
_markProofAsMissing(id, blocknumber);
|
_markProofAsMissing(id, blocknumber);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,14 +5,13 @@ import "./Stakes.sol";
|
||||||
|
|
||||||
// exposes internal functions of Stakes for testing
|
// exposes internal functions of Stakes for testing
|
||||||
contract TestStakes is Stakes {
|
contract TestStakes is Stakes {
|
||||||
|
|
||||||
constructor(IERC20 token) Stakes(token) {}
|
constructor(IERC20 token) Stakes(token) {}
|
||||||
|
|
||||||
function stake(address account) public view returns (uint) {
|
function stake(address account) public view returns (uint256) {
|
||||||
return _stake(account);
|
return _stake(account);
|
||||||
}
|
}
|
||||||
|
|
||||||
function increaseStake(uint amount) public {
|
function increaseStake(uint256 amount) public {
|
||||||
_increaseStake(amount);
|
_increaseStake(amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +27,7 @@ contract TestStakes is Stakes {
|
||||||
_unlockStake(account);
|
_unlockStake(account);
|
||||||
}
|
}
|
||||||
|
|
||||||
function slash(address account, uint percentage) public {
|
function slash(address account, uint256 percentage) public {
|
||||||
_slash(account, percentage);
|
_slash(account, percentage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||||
contract TestToken is ERC20 {
|
contract TestToken is ERC20 {
|
||||||
constructor() ERC20("TestToken", "TST") {}
|
constructor() ERC20("TestToken", "TST") {}
|
||||||
|
|
||||||
function mint(address holder, uint amount) public {
|
function mint(address holder, uint256 amount) public {
|
||||||
_mint(holder, amount);
|
_mint(holder, amount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
module.exports = async ({ deployments, getNamedAccounts }) => {
|
module.exports = async ({ deployments, getNamedAccounts }) => {
|
||||||
const token = await deployments.get('TestToken')
|
const token = await deployments.get("TestToken")
|
||||||
const stakeAmount = 100
|
const stakeAmount = 100
|
||||||
const slashMisses = 3
|
const slashMisses = 3
|
||||||
const slashPercentage = 10
|
const slashPercentage = 10
|
||||||
const args = [token.address, stakeAmount, slashMisses, slashPercentage]
|
const args = [token.address, stakeAmount, slashMisses, slashPercentage]
|
||||||
const { deployer } = await getNamedAccounts()
|
const { deployer } = await getNamedAccounts()
|
||||||
await deployments.deploy('Storage', { args, from: deployer })
|
await deployments.deploy("Storage", { args, from: deployer })
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.tags = ['Storage']
|
module.exports.tags = ["Storage"]
|
||||||
module.exports.dependencies = ['TestToken']
|
module.exports.dependencies = ["TestToken"]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
module.exports = async ({ deployments, getNamedAccounts }) => {
|
module.exports = async ({ deployments, getNamedAccounts }) => {
|
||||||
const { deployer } = await 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"
|
"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": {
|
"anymatch": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
|
||||||
|
@ -12982,11 +12988,112 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"prettier": {
|
"prettier": {
|
||||||
"version": "2.4.1",
|
"version": "2.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz",
|
||||||
"integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==",
|
"integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==",
|
||||||
"dev": true
|
"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": {
|
"printj": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz",
|
"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": {
|
"source-map": {
|
||||||
"version": "0.6.1",
|
"version": "0.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "hardhat test",
|
"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": {
|
"devDependencies": {
|
||||||
"@nomiclabs/hardhat-ethers": "^2.0.2",
|
"@nomiclabs/hardhat-ethers": "^2.0.2",
|
||||||
|
@ -14,6 +15,8 @@
|
||||||
"ethers": "^5.4.7",
|
"ethers": "^5.4.7",
|
||||||
"hardhat": "^2.6.5",
|
"hardhat": "^2.6.5",
|
||||||
"hardhat-deploy": "^0.9.8",
|
"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")
|
const { exampleRequest, exampleBid } = require("./examples")
|
||||||
|
|
||||||
describe("Contracts", function () {
|
describe("Contracts", function () {
|
||||||
|
|
||||||
const request = exampleRequest()
|
const request = exampleRequest()
|
||||||
const bid = exampleBid()
|
const bid = exampleBid()
|
||||||
|
|
||||||
|
@ -14,7 +13,7 @@ describe("Contracts", function () {
|
||||||
let id
|
let id
|
||||||
|
|
||||||
beforeEach(async function () {
|
beforeEach(async function () {
|
||||||
[client, host] = await ethers.getSigners()
|
;[client, host] = await ethers.getSigners()
|
||||||
let Contracts = await ethers.getContractFactory("TestContracts")
|
let Contracts = await ethers.getContractFactory("TestContracts")
|
||||||
contracts = await Contracts.deploy()
|
contracts = await Contracts.deploy()
|
||||||
requestHash = hashRequest(request)
|
requestHash = hashRequest(request)
|
||||||
|
@ -57,7 +56,8 @@ describe("Contracts", function () {
|
||||||
await sign(client, requestHash),
|
await sign(client, requestHash),
|
||||||
await sign(host, bidHash)
|
await sign(host, bidHash)
|
||||||
)
|
)
|
||||||
await expect(contracts.newContract(
|
await expect(
|
||||||
|
contracts.newContract(
|
||||||
request.duration,
|
request.duration,
|
||||||
request.size,
|
request.size,
|
||||||
request.contentHash,
|
request.contentHash,
|
||||||
|
@ -69,13 +69,18 @@ describe("Contracts", function () {
|
||||||
bid.bidExpiry,
|
bid.bidExpiry,
|
||||||
await sign(client, requestHash),
|
await sign(client, requestHash),
|
||||||
await sign(host, bidHash)
|
await sign(host, bidHash)
|
||||||
)).to.be.revertedWith("A contract with this id already exists")
|
)
|
||||||
|
).to.be.revertedWith("A contract with this id already exists")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("cannot be created when client signature is invalid", async function () {
|
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)
|
let invalidSignature = await sign(client, invalidHash)
|
||||||
await expect(contracts.newContract(
|
await expect(
|
||||||
|
contracts.newContract(
|
||||||
request.duration,
|
request.duration,
|
||||||
request.size,
|
request.size,
|
||||||
request.contentHash,
|
request.contentHash,
|
||||||
|
@ -87,13 +92,15 @@ describe("Contracts", function () {
|
||||||
bid.bidExpiry,
|
bid.bidExpiry,
|
||||||
invalidSignature,
|
invalidSignature,
|
||||||
await sign(host, bidHash)
|
await sign(host, bidHash)
|
||||||
)).to.be.revertedWith("Invalid signature")
|
)
|
||||||
|
).to.be.revertedWith("Invalid signature")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("cannot be created when host signature is invalid", async function () {
|
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)
|
let invalidSignature = await sign(host, invalidBid)
|
||||||
await expect(contracts.newContract(
|
await expect(
|
||||||
|
contracts.newContract(
|
||||||
request.duration,
|
request.duration,
|
||||||
request.size,
|
request.size,
|
||||||
request.contentHash,
|
request.contentHash,
|
||||||
|
@ -105,13 +112,15 @@ describe("Contracts", function () {
|
||||||
bid.bidExpiry,
|
bid.bidExpiry,
|
||||||
await sign(client, requestHash),
|
await sign(client, requestHash),
|
||||||
invalidSignature
|
invalidSignature
|
||||||
)).to.be.revertedWith("Invalid signature")
|
)
|
||||||
|
).to.be.revertedWith("Invalid signature")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("cannot be created when bid has expired", async function () {
|
it("cannot be created when bid has expired", async function () {
|
||||||
let expired = Math.round(Date.now() / 1000) - 60 // 1 minute ago
|
let expired = Math.round(Date.now() / 1000) - 60 // 1 minute ago
|
||||||
let bidHash = hashBid({ ...bid, requestHash, bidExpiry: expired })
|
let bidHash = hashBid({ ...bid, requestHash, bidExpiry: expired })
|
||||||
await expect(contracts.newContract(
|
await expect(
|
||||||
|
contracts.newContract(
|
||||||
request.duration,
|
request.duration,
|
||||||
request.size,
|
request.size,
|
||||||
request.contentHash,
|
request.contentHash,
|
||||||
|
@ -122,7 +131,8 @@ describe("Contracts", function () {
|
||||||
await host.getAddress(),
|
await host.getAddress(),
|
||||||
expired,
|
expired,
|
||||||
await sign(client, requestHash),
|
await sign(client, requestHash),
|
||||||
await sign(host, bidHash),
|
await sign(host, bidHash)
|
||||||
)).to.be.revertedWith("Bid expired")
|
)
|
||||||
|
).to.be.revertedWith("Bid expired")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,7 +3,6 @@ const { ethers } = require("hardhat")
|
||||||
const { mineBlock, minedBlockNumber } = require("./mining")
|
const { mineBlock, minedBlockNumber } = require("./mining")
|
||||||
|
|
||||||
describe("Proofs", function () {
|
describe("Proofs", function () {
|
||||||
|
|
||||||
const id = ethers.utils.randomBytes(32)
|
const id = ethers.utils.randomBytes(32)
|
||||||
const period = 10
|
const period = 10
|
||||||
const timeout = 5
|
const timeout = 5
|
||||||
|
@ -69,13 +68,12 @@ describe("Proofs", function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("when proofs are required", async function () {
|
describe("when proofs are required", async function () {
|
||||||
|
|
||||||
beforeEach(async function () {
|
beforeEach(async function () {
|
||||||
await proofs.expectProofs(id, period, timeout, duration)
|
await proofs.expectProofs(id, period, timeout, duration)
|
||||||
})
|
})
|
||||||
|
|
||||||
async function mineUntilProofIsRequired(id) {
|
async function mineUntilProofIsRequired(id) {
|
||||||
while (!await proofs.isProofRequired(id, await minedBlockNumber())) {
|
while (!(await proofs.isProofRequired(id, await minedBlockNumber()))) {
|
||||||
mineBlock()
|
mineBlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,7 +86,7 @@ describe("Proofs", function () {
|
||||||
|
|
||||||
async function mineUntilEnd() {
|
async function mineUntilEnd() {
|
||||||
const end = await proofs.end(id)
|
const end = await proofs.end(id)
|
||||||
while (await minedBlockNumber() < end) {
|
while ((await minedBlockNumber()) < end) {
|
||||||
mineBlock()
|
mineBlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,7 +94,8 @@ describe("Proofs", function () {
|
||||||
it("requires no proof for blocks that are unavailable", async function () {
|
it("requires no proof for blocks that are unavailable", async function () {
|
||||||
await mineUntilProofIsRequired(id)
|
await mineUntilProofIsRequired(id)
|
||||||
let blocknumber = await minedBlockNumber()
|
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()
|
mineBlock()
|
||||||
}
|
}
|
||||||
expect(await proofs.isProofRequired(id, blocknumber)).to.be.false
|
expect(await proofs.isProofRequired(id, blocknumber)).to.be.false
|
||||||
|
|
|
@ -2,13 +2,12 @@ const { expect } = require("chai")
|
||||||
const { ethers } = require("hardhat")
|
const { ethers } = require("hardhat")
|
||||||
|
|
||||||
describe("Stakes", function () {
|
describe("Stakes", function () {
|
||||||
|
|
||||||
var stakes
|
var stakes
|
||||||
var token
|
var token
|
||||||
var host
|
var host
|
||||||
|
|
||||||
beforeEach(async function () {
|
beforeEach(async function () {
|
||||||
[host] = await ethers.getSigners()
|
;[host] = await ethers.getSigners()
|
||||||
const Stakes = await ethers.getContractFactory("TestStakes")
|
const Stakes = await ethers.getContractFactory("TestStakes")
|
||||||
const TestToken = await ethers.getContractFactory("TestToken")
|
const TestToken = await ethers.getContractFactory("TestToken")
|
||||||
token = await TestToken.deploy()
|
token = await TestToken.deploy()
|
||||||
|
@ -30,9 +29,9 @@ describe("Stakes", function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it("does not increase stake when token transfer fails", async function () {
|
it("does not increase stake when token transfer fails", async function () {
|
||||||
await expect(
|
await expect(stakes.increaseStake(20)).to.be.revertedWith(
|
||||||
stakes.increaseStake(20)
|
"ERC20: transfer amount exceeds allowance"
|
||||||
).to.be.revertedWith("ERC20: transfer amount exceeds allowance")
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("allows withdrawal of stake", async function () {
|
it("allows withdrawal of stake", async function () {
|
||||||
|
@ -54,9 +53,9 @@ describe("Stakes", function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it("fails to unlock when already unlocked", async function () {
|
it("fails to unlock when already unlocked", async function () {
|
||||||
await expect(
|
await expect(stakes.unlockStake(host.address)).to.be.revertedWith(
|
||||||
stakes.unlockStake(host.address)
|
"Stake already unlocked"
|
||||||
).to.be.revertedWith("Stake already unlocked")
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("requires an equal amount of locks and unlocks", async function () {
|
it("requires an equal amount of locks and unlocks", async function () {
|
||||||
|
|
|
@ -5,7 +5,6 @@ const { exampleRequest, exampleBid } = require("./examples")
|
||||||
const { mineBlock, minedBlockNumber } = require("./mining")
|
const { mineBlock, minedBlockNumber } = require("./mining")
|
||||||
|
|
||||||
describe("Storage", function () {
|
describe("Storage", function () {
|
||||||
|
|
||||||
const request = exampleRequest()
|
const request = exampleRequest()
|
||||||
const bid = exampleBid()
|
const bid = exampleBid()
|
||||||
|
|
||||||
|
@ -15,10 +14,10 @@ describe("Storage", function () {
|
||||||
let stakeAmount, slashMisses, slashPercentage
|
let stakeAmount, slashMisses, slashPercentage
|
||||||
|
|
||||||
beforeEach(async function () {
|
beforeEach(async function () {
|
||||||
[client, host] = await ethers.getSigners()
|
;[client, host] = await ethers.getSigners()
|
||||||
await deployments.fixture(['TestToken', 'Storage'])
|
await deployments.fixture(["TestToken", "Storage"])
|
||||||
token = await ethers.getContract('TestToken')
|
token = await ethers.getContract("TestToken")
|
||||||
storage = await ethers.getContract('Storage')
|
storage = await ethers.getContract("Storage")
|
||||||
await token.mint(client.address, 1000)
|
await token.mint(client.address, 1000)
|
||||||
await token.mint(host.address, 1000)
|
await token.mint(host.address, 1000)
|
||||||
stakeAmount = await storage.stakeAmount()
|
stakeAmount = await storage.stakeAmount()
|
||||||
|
@ -27,7 +26,6 @@ describe("Storage", function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("creating a new storage contract", function () {
|
describe("creating a new storage contract", function () {
|
||||||
|
|
||||||
let id
|
let id
|
||||||
|
|
||||||
beforeEach(async function () {
|
beforeEach(async function () {
|
||||||
|
@ -63,9 +61,9 @@ describe("Storage", function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it("locks up host stake", async function () {
|
it("locks up host stake", async function () {
|
||||||
await expect(
|
await expect(storage.connect(host).withdrawStake()).to.be.revertedWith(
|
||||||
storage.connect(host).withdrawStake()
|
"Stake locked"
|
||||||
).to.be.revertedWith("Stake locked")
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("starting the contract", function () {
|
describe("starting the contract", function () {
|
||||||
|
@ -87,14 +85,13 @@ describe("Storage", function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("finishing the contract", function () {
|
describe("finishing the contract", function () {
|
||||||
|
|
||||||
beforeEach(async function () {
|
beforeEach(async function () {
|
||||||
await storage.connect(host).startContract(id)
|
await storage.connect(host).startContract(id)
|
||||||
})
|
})
|
||||||
|
|
||||||
async function mineUntilEnd() {
|
async function mineUntilEnd() {
|
||||||
const end = await storage.proofEnd(id)
|
const end = await storage.proofEnd(id)
|
||||||
while (await minedBlockNumber() < end) {
|
while ((await minedBlockNumber()) < end) {
|
||||||
await mineBlock()
|
await mineBlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,24 +111,23 @@ describe("Storage", function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it("is only allowed when end time has passed", async function () {
|
it("is only allowed when end time has passed", async function () {
|
||||||
await expect(
|
await expect(storage.finishContract(id)).to.be.revertedWith(
|
||||||
storage.finishContract(id)
|
"Contract has not ended yet"
|
||||||
).to.be.revertedWith("Contract has not ended yet")
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("can only be done once", async function () {
|
it("can only be done once", async function () {
|
||||||
await mineUntilEnd()
|
await mineUntilEnd()
|
||||||
await storage.finishContract(id)
|
await storage.finishContract(id)
|
||||||
await expect(
|
await expect(storage.finishContract(id)).to.be.revertedWith(
|
||||||
storage.finishContract(id)
|
"Contract already finished"
|
||||||
).to.be.revertedWith("Contract already finished")
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("slashing when missing proofs", function () {
|
describe("slashing when missing proofs", function () {
|
||||||
|
|
||||||
async function ensureProofIsMissing() {
|
async function ensureProofIsMissing() {
|
||||||
while (!await storage.isProofRequired(id, await minedBlockNumber())) {
|
while (!(await storage.isProofRequired(id, await minedBlockNumber()))) {
|
||||||
mineBlock()
|
mineBlock()
|
||||||
}
|
}
|
||||||
const blocknumber = await minedBlockNumber()
|
const blocknumber = await minedBlockNumber()
|
||||||
|
@ -146,7 +142,7 @@ describe("Storage", function () {
|
||||||
for (let i = 0; i < slashMisses; i++) {
|
for (let i = 0; i < slashMisses; i++) {
|
||||||
await ensureProofIsMissing()
|
await ensureProofIsMissing()
|
||||||
}
|
}
|
||||||
const expectedStake = stakeAmount * (100 - slashPercentage) / 100
|
const expectedStake = (stakeAmount * (100 - slashPercentage)) / 100
|
||||||
expect(await storage.stake(host.address)).to.equal(expectedStake)
|
expect(await storage.stake(host.address)).to.equal(expectedStake)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -158,7 +154,8 @@ describe("Storage", function () {
|
||||||
await storage.connect(host).increaseStake(stakeAmount - 1)
|
await storage.connect(host).increaseStake(stakeAmount - 1)
|
||||||
let requestHash = hashRequest(request)
|
let requestHash = hashRequest(request)
|
||||||
let bidHash = hashBid({ ...bid, requestHash })
|
let bidHash = hashBid({ ...bid, requestHash })
|
||||||
await expect(storage.newContract(
|
await expect(
|
||||||
|
storage.newContract(
|
||||||
request.duration,
|
request.duration,
|
||||||
request.size,
|
request.size,
|
||||||
request.contentHash,
|
request.contentHash,
|
||||||
|
@ -170,7 +167,8 @@ describe("Storage", function () {
|
||||||
bid.bidExpiry,
|
bid.bidExpiry,
|
||||||
await sign(client, requestHash),
|
await sign(client, requestHash),
|
||||||
await sign(host, bidHash)
|
await sign(host, bidHash)
|
||||||
)).to.be.revertedWith("Insufficient stake")
|
)
|
||||||
|
).to.be.revertedWith("Insufficient stake")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("doesn't create contract without payment of price", async function () {
|
it("doesn't create contract without payment of price", async function () {
|
||||||
|
@ -179,7 +177,8 @@ describe("Storage", function () {
|
||||||
await storage.connect(host).increaseStake(stakeAmount)
|
await storage.connect(host).increaseStake(stakeAmount)
|
||||||
let requestHash = hashRequest(request)
|
let requestHash = hashRequest(request)
|
||||||
let bidHash = hashBid({ ...bid, requestHash })
|
let bidHash = hashBid({ ...bid, requestHash })
|
||||||
await expect(storage.newContract(
|
await expect(
|
||||||
|
storage.newContract(
|
||||||
request.duration,
|
request.duration,
|
||||||
request.size,
|
request.size,
|
||||||
request.contentHash,
|
request.contentHash,
|
||||||
|
@ -191,7 +190,8 @@ describe("Storage", function () {
|
||||||
bid.bidExpiry,
|
bid.bidExpiry,
|
||||||
await sign(client, requestHash),
|
await sign(client, requestHash),
|
||||||
await sign(host, bidHash)
|
await sign(host, bidHash)
|
||||||
)).to.be.revertedWith("ERC20: transfer amount exceeds allowance")
|
)
|
||||||
|
).to.be.revertedWith("ERC20: transfer amount exceeds allowance")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -6,12 +6,12 @@ const exampleRequest = () => ({
|
||||||
contentHash: ethers.utils.sha256("0xdeadbeef"),
|
contentHash: ethers.utils.sha256("0xdeadbeef"),
|
||||||
proofPeriod: 8, // 8 blocks ≈ 2 minutes
|
proofPeriod: 8, // 8 blocks ≈ 2 minutes
|
||||||
proofTimeout: 4, // 4 blocks ≈ 1 minute
|
proofTimeout: 4, // 4 blocks ≈ 1 minute
|
||||||
nonce: ethers.utils.randomBytes(32)
|
nonce: ethers.utils.randomBytes(32),
|
||||||
})
|
})
|
||||||
|
|
||||||
const exampleBid = () => ({
|
const exampleBid = () => ({
|
||||||
price: 42,
|
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 }
|
module.exports = { exampleRequest, exampleBid }
|
||||||
|
|
|
@ -1,21 +1,30 @@
|
||||||
const { ethers } = require("hardhat")
|
const { ethers } = require("hardhat")
|
||||||
|
|
||||||
function hashRequest({
|
function hashRequest({
|
||||||
duration, size, contentHash, proofPeriod, proofTimeout, nonce
|
duration,
|
||||||
|
size,
|
||||||
|
contentHash,
|
||||||
|
proofPeriod,
|
||||||
|
proofTimeout,
|
||||||
|
nonce,
|
||||||
}) {
|
}) {
|
||||||
const type = "[dagger.request.v1]"
|
const type = "[dagger.request.v1]"
|
||||||
return ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(
|
return ethers.utils.keccak256(
|
||||||
|
ethers.utils.defaultAbiCoder.encode(
|
||||||
["string", "uint", "uint", "bytes32", "uint", "uint", "bytes32"],
|
["string", "uint", "uint", "bytes32", "uint", "uint", "bytes32"],
|
||||||
[type, duration, size, contentHash, proofPeriod, proofTimeout, nonce]
|
[type, duration, size, contentHash, proofPeriod, proofTimeout, nonce]
|
||||||
))
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function hashBid({ requestHash, bidExpiry, price }) {
|
function hashBid({ requestHash, bidExpiry, price }) {
|
||||||
const type = "[dagger.bid.v1]"
|
const type = "[dagger.bid.v1]"
|
||||||
return ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(
|
return ethers.utils.keccak256(
|
||||||
|
ethers.utils.defaultAbiCoder.encode(
|
||||||
["string", "bytes32", "uint", "uint"],
|
["string", "bytes32", "uint", "uint"],
|
||||||
[type, requestHash, bidExpiry, price]
|
[type, requestHash, bidExpiry, price]
|
||||||
))
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sign(signer, hash) {
|
async function sign(signer, hash) {
|
||||||
|
|
Loading…
Reference in New Issue