[Storage] Move markProofAsMissing() to Marketplace
This commit is contained in:
parent
a7397981bb
commit
6ea2b77a8f
|
@ -12,6 +12,10 @@ contract Marketplace is Collateral, Proofs {
|
||||||
using EnumerableSet for EnumerableSet.Bytes32Set;
|
using EnumerableSet for EnumerableSet.Bytes32Set;
|
||||||
|
|
||||||
uint256 public immutable collateral;
|
uint256 public immutable collateral;
|
||||||
|
uint256 public immutable minCollateralThreshold;
|
||||||
|
uint256 public immutable slashMisses;
|
||||||
|
uint256 public immutable slashPercentage;
|
||||||
|
|
||||||
MarketplaceFunds private funds;
|
MarketplaceFunds private funds;
|
||||||
mapping(RequestId => Request) private requests;
|
mapping(RequestId => Request) private requests;
|
||||||
mapping(RequestId => RequestContext) private requestContexts;
|
mapping(RequestId => RequestContext) private requestContexts;
|
||||||
|
@ -22,6 +26,9 @@ contract Marketplace is Collateral, Proofs {
|
||||||
constructor(
|
constructor(
|
||||||
IERC20 _token,
|
IERC20 _token,
|
||||||
uint256 _collateral,
|
uint256 _collateral,
|
||||||
|
uint256 _minCollateralThreshold,
|
||||||
|
uint256 _slashMisses,
|
||||||
|
uint256 _slashPercentage,
|
||||||
uint256 _proofPeriod,
|
uint256 _proofPeriod,
|
||||||
uint256 _proofTimeout,
|
uint256 _proofTimeout,
|
||||||
uint8 _proofDowntime
|
uint8 _proofDowntime
|
||||||
|
@ -31,6 +38,9 @@ contract Marketplace is Collateral, Proofs {
|
||||||
marketplaceInvariant
|
marketplaceInvariant
|
||||||
{
|
{
|
||||||
collateral = _collateral;
|
collateral = _collateral;
|
||||||
|
minCollateralThreshold = _minCollateralThreshold;
|
||||||
|
slashMisses = _slashMisses;
|
||||||
|
slashPercentage = _slashPercentage;
|
||||||
}
|
}
|
||||||
|
|
||||||
function myRequests() public view returns (RequestId[] memory) {
|
function myRequests() public view returns (RequestId[] memory) {
|
||||||
|
@ -119,6 +129,24 @@ contract Marketplace is Collateral, Proofs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function markProofAsMissing(SlotId slotId, uint256 period)
|
||||||
|
public
|
||||||
|
slotMustAcceptProofs(slotId)
|
||||||
|
{
|
||||||
|
_markProofAsMissing(slotId, period);
|
||||||
|
address host = _host(slotId);
|
||||||
|
if (missingProofs(slotId) % slashMisses == 0) {
|
||||||
|
_slash(host, slashPercentage);
|
||||||
|
|
||||||
|
if (balanceOf(host) < minCollateralThreshold) {
|
||||||
|
// When the collateral drops below the minimum threshold, the slot
|
||||||
|
// needs to be freed so that there is enough remaining collateral to be
|
||||||
|
// distributed for repairs and rewards (with any leftover to be burnt).
|
||||||
|
_forciblyFreeSlot(slotId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function _forciblyFreeSlot(SlotId slotId)
|
function _forciblyFreeSlot(SlotId slotId)
|
||||||
internal
|
internal
|
||||||
slotMustAcceptProofs(slotId)
|
slotMustAcceptProofs(slotId)
|
||||||
|
|
|
@ -6,11 +6,6 @@ import "./Proofs.sol";
|
||||||
import "./Collateral.sol";
|
import "./Collateral.sol";
|
||||||
|
|
||||||
contract Storage is Marketplace {
|
contract Storage is Marketplace {
|
||||||
uint256 public collateralAmount;
|
|
||||||
uint256 public slashMisses;
|
|
||||||
uint256 public slashPercentage;
|
|
||||||
uint256 public minCollateralThreshold;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
IERC20 token,
|
IERC20 token,
|
||||||
uint256 _proofPeriod,
|
uint256 _proofPeriod,
|
||||||
|
@ -24,32 +19,12 @@ contract Storage is Marketplace {
|
||||||
Marketplace(
|
Marketplace(
|
||||||
token,
|
token,
|
||||||
_collateralAmount,
|
_collateralAmount,
|
||||||
|
_minCollateralThreshold,
|
||||||
|
_slashMisses,
|
||||||
|
_slashPercentage,
|
||||||
_proofPeriod,
|
_proofPeriod,
|
||||||
_proofTimeout,
|
_proofTimeout,
|
||||||
_proofDowntime
|
_proofDowntime
|
||||||
)
|
)
|
||||||
{
|
{}
|
||||||
collateralAmount = _collateralAmount;
|
|
||||||
slashMisses = _slashMisses;
|
|
||||||
slashPercentage = _slashPercentage;
|
|
||||||
minCollateralThreshold = _minCollateralThreshold;
|
|
||||||
}
|
|
||||||
|
|
||||||
function markProofAsMissing(SlotId slotId, uint256 period)
|
|
||||||
public
|
|
||||||
slotMustAcceptProofs(slotId)
|
|
||||||
{
|
|
||||||
_markProofAsMissing(slotId, period);
|
|
||||||
address host = _host(slotId);
|
|
||||||
if (missingProofs(slotId) % slashMisses == 0) {
|
|
||||||
_slash(host, slashPercentage);
|
|
||||||
|
|
||||||
if (balanceOf(host) < minCollateralThreshold) {
|
|
||||||
// When the collateral drops below the minimum threshold, the slot
|
|
||||||
// needs to be freed so that there is enough remaining collateral to be
|
|
||||||
// distributed for repairs and rewards (with any leftover to be burnt).
|
|
||||||
_forciblyFreeSlot(slotId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,9 @@ contract TestMarketplace is Marketplace {
|
||||||
constructor(
|
constructor(
|
||||||
IERC20 _token,
|
IERC20 _token,
|
||||||
uint256 _collateral,
|
uint256 _collateral,
|
||||||
|
uint256 _minCollateralThreshold,
|
||||||
|
uint256 _slashMisses,
|
||||||
|
uint256 _slashPercentage,
|
||||||
uint256 _proofPeriod,
|
uint256 _proofPeriod,
|
||||||
uint256 _proofTimeout,
|
uint256 _proofTimeout,
|
||||||
uint8 _proofDowntime
|
uint8 _proofDowntime
|
||||||
|
@ -15,6 +18,9 @@ contract TestMarketplace is Marketplace {
|
||||||
Marketplace(
|
Marketplace(
|
||||||
_token,
|
_token,
|
||||||
_collateral,
|
_collateral,
|
||||||
|
_minCollateralThreshold,
|
||||||
|
_slashMisses,
|
||||||
|
_slashPercentage,
|
||||||
_proofPeriod,
|
_proofPeriod,
|
||||||
_proofTimeout,
|
_proofTimeout,
|
||||||
_proofDowntime
|
_proofDowntime
|
||||||
|
@ -40,7 +46,9 @@ contract TestMarketplace is Marketplace {
|
||||||
return _slot(slotId);
|
return _slot(slotId);
|
||||||
}
|
}
|
||||||
|
|
||||||
function testAcceptsProofs(SlotId slotId)
|
function testAcceptsProofs(
|
||||||
|
SlotId slotId
|
||||||
|
)
|
||||||
public
|
public
|
||||||
view
|
view
|
||||||
slotMustAcceptProofs(slotId)
|
slotMustAcceptProofs(slotId)
|
||||||
|
|
|
@ -27,6 +27,9 @@ const {
|
||||||
|
|
||||||
describe("Marketplace", function () {
|
describe("Marketplace", function () {
|
||||||
const collateral = 100
|
const collateral = 100
|
||||||
|
const minCollateralThreshold = 40
|
||||||
|
const slashMisses = 3
|
||||||
|
const slashPercentage = 10
|
||||||
const proofPeriod = 10
|
const proofPeriod = 10
|
||||||
const proofTimeout = 5
|
const proofTimeout = 5
|
||||||
const proofDowntime = 64
|
const proofDowntime = 64
|
||||||
|
@ -54,6 +57,9 @@ describe("Marketplace", function () {
|
||||||
marketplace = await Marketplace.deploy(
|
marketplace = await Marketplace.deploy(
|
||||||
token.address,
|
token.address,
|
||||||
collateral,
|
collateral,
|
||||||
|
minCollateralThreshold,
|
||||||
|
slashMisses,
|
||||||
|
slashPercentage,
|
||||||
proofPeriod,
|
proofPeriod,
|
||||||
proofTimeout,
|
proofTimeout,
|
||||||
proofDowntime
|
proofDowntime
|
||||||
|
@ -752,6 +758,86 @@ describe("Marketplace", function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("missing proofs", function () {
|
||||||
|
let period, periodOf, periodEnd
|
||||||
|
|
||||||
|
beforeEach(async function () {
|
||||||
|
period = (await marketplace.proofPeriod()).toNumber()
|
||||||
|
;({ periodOf, periodEnd } = periodic(period))
|
||||||
|
|
||||||
|
switchAccount(client)
|
||||||
|
await token.approve(marketplace.address, price(request))
|
||||||
|
await marketplace.requestStorage(request)
|
||||||
|
switchAccount(host)
|
||||||
|
await token.approve(marketplace.address, collateral)
|
||||||
|
await marketplace.deposit(collateral)
|
||||||
|
})
|
||||||
|
|
||||||
|
async function waitUntilProofIsRequired(id) {
|
||||||
|
await advanceTimeTo(periodEnd(periodOf(await currentTime())))
|
||||||
|
while (
|
||||||
|
!(
|
||||||
|
(await marketplace.isProofRequired(id)) &&
|
||||||
|
(await marketplace.getPointer(id)) < 250
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
await advanceTime(period)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
it("fails to mark proof as missing when cancelled", async function () {
|
||||||
|
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||||
|
await waitUntilCancelled(request)
|
||||||
|
let missedPeriod = periodOf(await currentTime())
|
||||||
|
await expect(
|
||||||
|
marketplace.markProofAsMissing(slotId(slot), missedPeriod)
|
||||||
|
).to.be.revertedWith("Slot not accepting proofs")
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("slashing when missing proofs", function () {
|
||||||
|
it("reduces collateral when too many proofs are missing", async function () {
|
||||||
|
const id = slotId(slot)
|
||||||
|
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||||
|
for (let i = 0; i < slashMisses; i++) {
|
||||||
|
await waitUntilProofIsRequired(id)
|
||||||
|
let missedPeriod = periodOf(await currentTime())
|
||||||
|
await advanceTime(period)
|
||||||
|
await marketplace.markProofAsMissing(id, missedPeriod)
|
||||||
|
}
|
||||||
|
const expectedBalance = (collateral * (100 - slashPercentage)) / 100
|
||||||
|
expect(await marketplace.balanceOf(host.address)).to.equal(
|
||||||
|
expectedBalance
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("freeing a slot", function () {
|
||||||
|
it("frees slot when collateral slashed below minimum threshold", async function () {
|
||||||
|
const id = slotId(slot)
|
||||||
|
|
||||||
|
await waitUntilStarted(marketplace, request, proof)
|
||||||
|
|
||||||
|
// max slashes before dropping below collateral threshold
|
||||||
|
const maxSlashes = 10
|
||||||
|
for (let i = 0; i < maxSlashes; i++) {
|
||||||
|
for (let j = 0; j < slashMisses; j++) {
|
||||||
|
await waitUntilProofIsRequired(id)
|
||||||
|
let missedPeriod = periodOf(await currentTime())
|
||||||
|
await advanceTime(period)
|
||||||
|
if (i === maxSlashes - 1 && j === slashMisses - 1) {
|
||||||
|
await expect(
|
||||||
|
await marketplace.markProofAsMissing(id, missedPeriod)
|
||||||
|
).to.emit(marketplace, "SlotFreed")
|
||||||
|
expect(await marketplace.getHost(id)).to.equal(AddressZero)
|
||||||
|
} else {
|
||||||
|
await marketplace.markProofAsMissing(id, missedPeriod)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe("modifiers", function () {
|
describe("modifiers", function () {
|
||||||
beforeEach(async function () {
|
beforeEach(async function () {
|
||||||
switchAccount(client)
|
switchAccount(client)
|
||||||
|
|
|
@ -38,7 +38,7 @@ describe("Storage", function () {
|
||||||
await token.mint(client.address, 1_000_000_000)
|
await token.mint(client.address, 1_000_000_000)
|
||||||
await token.mint(host.address, 1_000_000_000)
|
await token.mint(host.address, 1_000_000_000)
|
||||||
|
|
||||||
collateralAmount = await storage.collateralAmount()
|
collateralAmount = await storage.collateral()
|
||||||
slashMisses = await storage.slashMisses()
|
slashMisses = await storage.slashMisses()
|
||||||
slashPercentage = await storage.slashPercentage()
|
slashPercentage = await storage.slashPercentage()
|
||||||
minCollateralThreshold = await storage.minCollateralThreshold()
|
minCollateralThreshold = await storage.minCollateralThreshold()
|
||||||
|
@ -66,78 +66,6 @@ describe("Storage", function () {
|
||||||
await expect(storage.withdraw()).not.to.be.reverted
|
await expect(storage.withdraw()).not.to.be.reverted
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("missing proofs", function () {
|
|
||||||
let period, periodOf, periodEnd
|
|
||||||
|
|
||||||
beforeEach(async function () {
|
|
||||||
period = (await storage.proofPeriod()).toNumber()
|
|
||||||
;({ periodOf, periodEnd } = periodic(period))
|
|
||||||
})
|
|
||||||
|
|
||||||
async function waitUntilProofIsRequired(id) {
|
|
||||||
await advanceTimeTo(periodEnd(periodOf(await currentTime())))
|
|
||||||
while (
|
|
||||||
!(
|
|
||||||
(await storage.isProofRequired(id)) &&
|
|
||||||
(await storage.getPointer(id)) < 250
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
await advanceTime(period)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
it("fails to mark proof as missing when cancelled", async function () {
|
|
||||||
await storage.fillSlot(slot.request, slot.index, proof)
|
|
||||||
await waitUntilCancelled(request)
|
|
||||||
let missedPeriod = periodOf(await currentTime())
|
|
||||||
await expect(
|
|
||||||
storage.markProofAsMissing(slotId(slot), missedPeriod)
|
|
||||||
).to.be.revertedWith("Slot not accepting proofs")
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("slashing when missing proofs", function () {
|
|
||||||
it("reduces collateral when too many proofs are missing", async function () {
|
|
||||||
const id = slotId(slot)
|
|
||||||
await storage.fillSlot(slot.request, slot.index, proof)
|
|
||||||
for (let i = 0; i < slashMisses; i++) {
|
|
||||||
await waitUntilProofIsRequired(id)
|
|
||||||
let missedPeriod = periodOf(await currentTime())
|
|
||||||
await advanceTime(period)
|
|
||||||
await storage.markProofAsMissing(id, missedPeriod)
|
|
||||||
}
|
|
||||||
const expectedBalance =
|
|
||||||
(collateralAmount * (100 - slashPercentage)) / 100
|
|
||||||
expect(await storage.balanceOf(host.address)).to.equal(expectedBalance)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("freeing a slot", function () {
|
|
||||||
it("frees slot when collateral slashed below minimum threshold", async function () {
|
|
||||||
const id = slotId(slot)
|
|
||||||
|
|
||||||
await waitUntilStarted(storage, request, proof)
|
|
||||||
|
|
||||||
// max slashes before dropping below collateral threshold
|
|
||||||
const maxSlashes = 10
|
|
||||||
for (let i = 0; i < maxSlashes; i++) {
|
|
||||||
for (let j = 0; j < slashMisses; j++) {
|
|
||||||
await waitUntilProofIsRequired(id)
|
|
||||||
let missedPeriod = periodOf(await currentTime())
|
|
||||||
await advanceTime(period)
|
|
||||||
if (i === maxSlashes - 1 && j === slashMisses - 1) {
|
|
||||||
await expect(
|
|
||||||
await storage.markProofAsMissing(id, missedPeriod)
|
|
||||||
).to.emit(storage, "SlotFreed")
|
|
||||||
expect(await storage.getHost(id)).to.equal(AddressZero)
|
|
||||||
} else {
|
|
||||||
await storage.markProofAsMissing(id, missedPeriod)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO: implement checking of actual proofs of storage, instead of dummy bool
|
// TODO: implement checking of actual proofs of storage, instead of dummy bool
|
||||||
|
|
Loading…
Reference in New Issue