[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;
|
||||
|
||||
uint256 public immutable collateral;
|
||||
uint256 public immutable minCollateralThreshold;
|
||||
uint256 public immutable slashMisses;
|
||||
uint256 public immutable slashPercentage;
|
||||
|
||||
MarketplaceFunds private funds;
|
||||
mapping(RequestId => Request) private requests;
|
||||
mapping(RequestId => RequestContext) private requestContexts;
|
||||
|
@ -22,6 +26,9 @@ contract Marketplace is Collateral, Proofs {
|
|||
constructor(
|
||||
IERC20 _token,
|
||||
uint256 _collateral,
|
||||
uint256 _minCollateralThreshold,
|
||||
uint256 _slashMisses,
|
||||
uint256 _slashPercentage,
|
||||
uint256 _proofPeriod,
|
||||
uint256 _proofTimeout,
|
||||
uint8 _proofDowntime
|
||||
|
@ -31,6 +38,9 @@ contract Marketplace is Collateral, Proofs {
|
|||
marketplaceInvariant
|
||||
{
|
||||
collateral = _collateral;
|
||||
minCollateralThreshold = _minCollateralThreshold;
|
||||
slashMisses = _slashMisses;
|
||||
slashPercentage = _slashPercentage;
|
||||
}
|
||||
|
||||
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)
|
||||
internal
|
||||
slotMustAcceptProofs(slotId)
|
||||
|
|
|
@ -6,11 +6,6 @@ import "./Proofs.sol";
|
|||
import "./Collateral.sol";
|
||||
|
||||
contract Storage is Marketplace {
|
||||
uint256 public collateralAmount;
|
||||
uint256 public slashMisses;
|
||||
uint256 public slashPercentage;
|
||||
uint256 public minCollateralThreshold;
|
||||
|
||||
constructor(
|
||||
IERC20 token,
|
||||
uint256 _proofPeriod,
|
||||
|
@ -24,32 +19,12 @@ contract Storage is Marketplace {
|
|||
Marketplace(
|
||||
token,
|
||||
_collateralAmount,
|
||||
_minCollateralThreshold,
|
||||
_slashMisses,
|
||||
_slashPercentage,
|
||||
_proofPeriod,
|
||||
_proofTimeout,
|
||||
_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(
|
||||
IERC20 _token,
|
||||
uint256 _collateral,
|
||||
uint256 _minCollateralThreshold,
|
||||
uint256 _slashMisses,
|
||||
uint256 _slashPercentage,
|
||||
uint256 _proofPeriod,
|
||||
uint256 _proofTimeout,
|
||||
uint8 _proofDowntime
|
||||
|
@ -15,6 +18,9 @@ contract TestMarketplace is Marketplace {
|
|||
Marketplace(
|
||||
_token,
|
||||
_collateral,
|
||||
_minCollateralThreshold,
|
||||
_slashMisses,
|
||||
_slashPercentage,
|
||||
_proofPeriod,
|
||||
_proofTimeout,
|
||||
_proofDowntime
|
||||
|
@ -40,7 +46,9 @@ contract TestMarketplace is Marketplace {
|
|||
return _slot(slotId);
|
||||
}
|
||||
|
||||
function testAcceptsProofs(SlotId slotId)
|
||||
function testAcceptsProofs(
|
||||
SlotId slotId
|
||||
)
|
||||
public
|
||||
view
|
||||
slotMustAcceptProofs(slotId)
|
||||
|
|
|
@ -27,6 +27,9 @@ const {
|
|||
|
||||
describe("Marketplace", function () {
|
||||
const collateral = 100
|
||||
const minCollateralThreshold = 40
|
||||
const slashMisses = 3
|
||||
const slashPercentage = 10
|
||||
const proofPeriod = 10
|
||||
const proofTimeout = 5
|
||||
const proofDowntime = 64
|
||||
|
@ -54,6 +57,9 @@ describe("Marketplace", function () {
|
|||
marketplace = await Marketplace.deploy(
|
||||
token.address,
|
||||
collateral,
|
||||
minCollateralThreshold,
|
||||
slashMisses,
|
||||
slashPercentage,
|
||||
proofPeriod,
|
||||
proofTimeout,
|
||||
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 () {
|
||||
beforeEach(async function () {
|
||||
switchAccount(client)
|
||||
|
|
|
@ -38,7 +38,7 @@ describe("Storage", function () {
|
|||
await token.mint(client.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()
|
||||
slashPercentage = await storage.slashPercentage()
|
||||
minCollateralThreshold = await storage.minCollateralThreshold()
|
||||
|
@ -66,78 +66,6 @@ describe("Storage", function () {
|
|||
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
|
||||
|
|
Loading…
Reference in New Issue