[marketplace] free slot after too many proofs missed

Needs tests
This commit is contained in:
Eric Mastro 2022-09-13 17:14:57 +10:00 committed by Eric Mastro
parent fdc0e7d58a
commit 7487663534
6 changed files with 76 additions and 8 deletions

View File

@ -64,6 +64,25 @@ contract AccountLocks {
require(accountLocks.length == 0, "Account locked"); require(accountLocks.length == 0, "Account locked");
} }
/// Removes an account lock
function _removeAccountLock(address account, bytes32 lockId) internal {
require(accounts[account].locks.length > 0, "Account lock doesn't exist");
bytes32[] storage accountLocks = accounts[account].locks;
uint256 index = 0;
while (true) {
if (index >= accountLocks.length) {
return;
}
if (accountLocks[index] == lockId) {
accountLocks[index] = accountLocks[accountLocks.length - 1];
accountLocks.pop();
} else {
index++;
}
}
}
function removeInactiveLocks(bytes32[] storage lockIds) private { function removeInactiveLocks(bytes32[] storage lockIds) private {
uint256 index = 0; uint256 index = 0;
while (true) { while (true) {

View File

@ -51,6 +51,12 @@ contract Collateral is AccountLocks {
internal internal
collateralInvariant collateralInvariant
{ {
// TODO: perhaps we need to add a minCollateral parameter so that
// a host's collateral can't drop below a certain amount, possibly
// preventing malicious behaviour when collateral drops too low for it
// to matter that it will be lost. Also, we need collateral to be high
// enough to cover repair costs in case of repair as well as marked
// proofs as missing fees.
uint256 amount = (balanceOf(account) * percentage) / 100; uint256 amount = (balanceOf(account) * percentage) / 100;
funds.slashed += amount; funds.slashed += amount;
subtract(account, amount); subtract(account, amount);

View File

@ -47,13 +47,36 @@ contract Marketplace is Collateral, Proofs {
emit StorageRequested(id, request.ask); emit StorageRequested(id, request.ask);
} }
function _freeSlot(
bytes32 slotId
) internal marketplaceInvariant {
bytes32 requestId = _getRequestIdForSlot(slotId);
RequestContext storage context = requestContexts[requestId];
require(context.state == RequestState.Started, "Invalid state");
require(!_isCancelled(requestId), "Request cancelled");
Slot storage slot = _slot(slotId);
require(slot.host != address(0), "Slot not filled");
_removeAccountLock(slot.host, requestId);
// TODO: burn host's collateral except for repair costs + mark proof
// missing reward
_unexpectProofs(slotId);
slot.host = address(0);
slot.requestId = 0;
context.slotsFilled -= 1;
emit SlotFreed(requestId, slotId);
}
function fillSlot( function fillSlot(
bytes32 requestId, bytes32 requestId,
uint256 slotIndex, uint256 slotIndex,
bytes calldata proof bytes calldata proof
) public marketplaceInvariant { ) public marketplaceInvariant {
Request storage request = requests[requestId]; Request storage request = _request(requestId);
require(request.client != address(0), "Unknown request"); // TODO: change below to check !_isCancelled(requestId) instead?
require(request.expiry > block.timestamp, "Request expired"); require(request.expiry > block.timestamp, "Request expired");
require(slotIndex < request.ask.slots, "Invalid slot"); require(slotIndex < request.ask.slots, "Invalid slot");
RequestContext storage context = requestContexts[requestId]; RequestContext storage context = requestContexts[requestId];
@ -160,11 +183,13 @@ contract Marketplace is Collateral, Proofs {
} }
function _request(bytes32 id) internal view returns (Request storage) { function _request(bytes32 id) internal view returns (Request storage) {
Request storage request = requests[id];
require(request.client != address(0), "Unknown request");
return requests[id]; return requests[id];
} }
function _slot(bytes32 slotId) internal view returns (Slot memory) { function _slot(bytes32 slotId) internal view returns (Slot storage) {
Slot memory slot = slots[slotId]; Slot storage slot = slots[slotId];
require(slot.host != address(0), "Slot empty"); require(slot.host != address(0), "Slot empty");
return slot; return slot;
} }
@ -292,8 +317,9 @@ contract Marketplace is Collateral, Proofs {
event SlotFilled( event SlotFilled(
bytes32 indexed requestId, bytes32 indexed requestId,
uint256 indexed slotIndex, uint256 indexed slotIndex,
bytes32 indexed slotId bytes32 slotId
); );
event SlotFreed(bytes32 indexed requestId, bytes32 slotId);
event RequestCancelled(bytes32 indexed requestId); event RequestCancelled(bytes32 indexed requestId);
modifier marketplaceInvariant() { modifier marketplaceInvariant() {

View File

@ -63,6 +63,13 @@ contract Proofs {
markers[id] = uint256(blockhash(block.number - 1)) % period; markers[id] = uint256(blockhash(block.number - 1)) % period;
} }
function _unexpectProofs(
bytes32 id
) internal {
require(ids[id], "Proof id not in use");
ids[id] = false;
}
function _getPointer(bytes32 id, uint256 proofPeriod) function _getPointer(bytes32 id, uint256 proofPeriod)
internal internal
view view
@ -111,7 +118,8 @@ contract Proofs {
pointer = _getPointer(id, proofPeriod); pointer = _getPointer(id, proofPeriod);
bytes32 challenge = _getChallenge(pointer); bytes32 challenge = _getChallenge(pointer);
uint256 probability = (probabilities[id] * (256 - downtime)) / 256; uint256 probability = (probabilities[id] * (256 - downtime)) / 256;
isRequired = uint256(challenge) % probability == 0; // TODO: add test for below change
isRequired = ids[id] && uint256(challenge) % probability == 0;
} }
function _isProofRequired(bytes32 id, uint256 proofPeriod) function _isProofRequired(bytes32 id, uint256 proofPeriod)

View File

@ -9,6 +9,7 @@ contract Storage is Collateral, Marketplace {
uint256 public collateralAmount; uint256 public collateralAmount;
uint256 public slashMisses; uint256 public slashMisses;
uint256 public slashPercentage; uint256 public slashPercentage;
uint256 public missThreshold;
constructor( constructor(
IERC20 token, IERC20 token,
@ -17,7 +18,8 @@ contract Storage is Collateral, Marketplace {
uint8 _proofDowntime, uint8 _proofDowntime,
uint256 _collateralAmount, uint256 _collateralAmount,
uint256 _slashMisses, uint256 _slashMisses,
uint256 _slashPercentage uint256 _slashPercentage,
uint256 _missThreshold
) )
Marketplace( Marketplace(
token, token,
@ -30,6 +32,7 @@ contract Storage is Collateral, Marketplace {
collateralAmount = _collateralAmount; collateralAmount = _collateralAmount;
slashMisses = _slashMisses; slashMisses = _slashMisses;
slashPercentage = _slashPercentage; slashPercentage = _slashPercentage;
missThreshold = _missThreshold;
} }
function getRequest(bytes32 requestId) public view returns (Request memory) { function getRequest(bytes32 requestId) public view returns (Request memory) {
@ -82,8 +85,12 @@ contract Storage is Collateral, Marketplace {
slotMustAcceptProofs(slotId) slotMustAcceptProofs(slotId)
{ {
_markProofAsMissing(slotId, period); _markProofAsMissing(slotId, period);
if (_missed(slotId) % slashMisses == 0) { uint256 missed = _missed(slotId);
if (missed % slashMisses == 0) {
_slash(_host(slotId), slashPercentage); _slash(_host(slotId), slashPercentage);
} }
if (missed > missThreshold) {
_freeSlot(slotId);
}
} }
} }

View File

@ -6,6 +6,7 @@ async function deployStorage({ deployments, getNamedAccounts }) {
const collateralAmount = 100 const collateralAmount = 100
const slashMisses = 3 const slashMisses = 3
const slashPercentage = 10 const slashPercentage = 10
const missThreshold = 20
const args = [ const args = [
token.address, token.address,
proofPeriod, proofPeriod,
@ -14,6 +15,7 @@ async function deployStorage({ deployments, getNamedAccounts }) {
collateralAmount, collateralAmount,
slashMisses, slashMisses,
slashPercentage, slashPercentage,
missThreshold,
] ]
const { deployer } = await getNamedAccounts() const { deployer } = await getNamedAccounts()
await deployments.deploy("Storage", { args, from: deployer }) await deployments.deploy("Storage", { args, from: deployer })