diff --git a/contracts/Configuration.sol b/contracts/Configuration.sol index 0b95f0a..894039e 100644 --- a/contracts/Configuration.sol +++ b/contracts/Configuration.sol @@ -14,7 +14,6 @@ struct CollateralConfig { /// TODO: to be aligned more closely with actual cost of repair once bandwidth incentives are known, /// see https://github.com/codex-storage/codex-contracts-eth/pull/47#issuecomment-1465511949. uint8 repairRewardPercentage; - uint8 maxNumberOfSlashes; // frees slot when the number of slashing reaches this value uint16 slashCriterion; // amount of proofs missed that lead to slashing uint8 slashPercentage; // percentage of the collateral that is slashed diff --git a/contracts/Marketplace.sol b/contracts/Marketplace.sol index 48d5843..d27b5fe 100644 --- a/contracts/Marketplace.sol +++ b/contracts/Marketplace.sol @@ -26,7 +26,6 @@ contract Marketplace is Proofs, StateRetrieval { struct RequestContext { RequestState state; uint256 slotsFilled; - /// @notice Tracks how much funds should be returned when Request expires to the Request creator /// @dev The sum is deducted every time a host fills a Slot by precalculated amount that he should receive if the Request expires uint256 expiryFundsWithdraw; @@ -37,12 +36,10 @@ contract Marketplace is Proofs, StateRetrieval { struct Slot { SlotState state; RequestId requestId; - /// @notice Timestamp that signals when slot was filled /// @dev Used for partial payouts when Requests expires and Hosts are paid out only the time they host the content. uint256 filledAt; uint256 slotIndex; - /// @notice Tracks the current amount of host's collateral that is to be payed out at the end of Slot's lifespan. /// @dev When Slot is filled, the collateral is collected in amount of request.ask.collateral /// @dev When Host is slashed for missing a proof the slashed amount is reflected in this variable @@ -116,15 +113,18 @@ contract Marketplace is Proofs, StateRetrieval { require(slotState(slotId) == SlotState.Free, "Slot is not free"); _startRequiringProofs(slotId, request.ask.proofProbability); - // TODO: Update call signature -// submitProof(slotId, proof); + // TODO: Update call signature + // submitProof(slotId, proof); slot.host = msg.sender; slot.state = SlotState.Filled; slot.filledAt = block.timestamp; RequestContext storage context = _requestContexts[requestId]; context.slotsFilled += 1; - context.expiryFundsWithdraw -= _expiryPayoutAmount(requestId, block.timestamp); + context.expiryFundsWithdraw -= _expiryPayoutAmount( + requestId, + block.timestamp + ); // Collect collateral uint256 collateralAmount = request.ask.collateral; @@ -233,7 +233,8 @@ contract Marketplace is Proofs, StateRetrieval { Slot storage slot = _slots[slotId]; _removeFromMySlots(slot.host, slotId); - uint256 amount = _expiryPayoutAmount(requestId, slot.filledAt) + slot.currentCollateral; + uint256 amount = _expiryPayoutAmount(requestId, slot.filledAt) + + slot.currentCollateral; _marketplaceTotals.sent += amount; slot.state = SlotState.Paid; assert(token.transfer(slot.host, amount)); @@ -298,7 +299,10 @@ contract Marketplace is Proofs, StateRetrieval { } /// @notice Calculates the amount that should be payed out to a host if a request expires based on when the host fills the slot - function _expiryPayoutAmount(RequestId requestId, uint256 startingTimestamp) private view returns (uint256) { + function _expiryPayoutAmount( + RequestId requestId, + uint256 startingTimestamp + ) private view returns (uint256) { Request storage request = _requests[requestId]; require(startingTimestamp < request.expiry, "Start not before expiry"); @@ -356,14 +360,8 @@ contract Marketplace is Proofs, StateRetrieval { event StorageRequested(RequestId requestId, Ask ask, uint256 expiry); event RequestFulfilled(RequestId indexed requestId); event RequestFailed(RequestId indexed requestId); - event SlotFilled( - RequestId indexed requestId, - uint256 slotIndex - ); - event SlotFreed( - RequestId indexed requestId, - uint256 slotIndex - ); + event SlotFilled(RequestId indexed requestId, uint256 slotIndex); + event SlotFreed(RequestId indexed requestId, uint256 slotIndex); event RequestCancelled(RequestId indexed requestId); struct MarketplaceTotals { diff --git a/contracts/Proofs.sol b/contracts/Proofs.sol index 94287d3..f0253b2 100644 --- a/contracts/Proofs.sol +++ b/contracts/Proofs.sol @@ -10,7 +10,10 @@ abstract contract Proofs is Periods { ProofConfig private _config; IVerifier private _verifier; - constructor(ProofConfig memory config, IVerifier verifier) Periods(config.period) { + constructor( + ProofConfig memory config, + IVerifier verifier + ) Periods(config.period) { require(block.number > 256, "Insufficient block height"); _config = config; _verifier = verifier; @@ -109,7 +112,13 @@ abstract contract Proofs is Periods { // - external entropy (for example some fresh ethereum block header) - this gives us the unbiased randomness we use to sample which cells to prove // - the dataset root (which dataset we prove) // - and the slot index (which slot out of that dataset we prove) - function submitProof(SlotId id, uint[2] calldata pA, uint[2][2] calldata pB, uint[2] calldata pC, uint[3] calldata pubSignals) public { + function submitProof( + SlotId id, + uint[2] calldata pA, + uint[2][2] calldata pB, + uint[2] calldata pC, + uint[3] calldata pubSignals + ) public { require(!_received[id][_blockPeriod()], "Proof already submitted"); require(_verifier.verifyProof(pA, pB, pC, pubSignals), "Invalid proof"); _received[id][_blockPeriod()] = true; diff --git a/test/Marketplace.test.js b/test/Marketplace.test.js index 7242ebb..25b7887 100644 --- a/test/Marketplace.test.js +++ b/test/Marketplace.test.js @@ -164,7 +164,7 @@ describe("Marketplace", function () { }) it("rejects request when expiry is after request end", async function () { - request.expiry = await currentTime() + request.ask.duration + hours(1) + request.expiry = (await currentTime()) + request.ask.duration + hours(1) await token.approve(marketplace.address, price(request)) await expect(marketplace.requestStorage(request)).to.be.revertedWith( "Request end before expiry" @@ -427,7 +427,10 @@ describe("Marketplace", function () { it("pays the host when contract was cancelled", async function () { // Lets move the time into middle of the expiry window - const fillTimestamp = await currentTime() + Math.floor((request.expiry - await currentTime()) / 2) - 1 + const fillTimestamp = + (await currentTime()) + + Math.floor((request.expiry - (await currentTime())) / 2) - + 1 await advanceTimeToForNextBlock(fillTimestamp) await marketplace.fillSlot(slot.request, slot.index, proof) @@ -435,8 +438,11 @@ describe("Marketplace", function () { await marketplace.freeSlot(slotId(slot)) const endBalance = await token.balanceOf(host.address) - const expectedPartialPayout = (request.expiry - fillTimestamp) * request.ask.reward - expect(endBalance - ACCOUNT_STARTING_BALANCE).to.be.equal(expectedPartialPayout) + const expectedPartialPayout = + (request.expiry - fillTimestamp) * request.ask.reward + expect(endBalance - ACCOUNT_STARTING_BALANCE).to.be.equal( + expectedPartialPayout + ) }) it("does not pay when the contract hasn't ended", async function () { @@ -572,16 +578,21 @@ describe("Marketplace", function () { }) it("withdraws to the client for cancelled requests lowered by hosts payout", async function () { - const fillTimestamp = await currentTime() + Math.floor((request.expiry - await currentTime()) / 2) + const fillTimestamp = + (await currentTime()) + + Math.floor((request.expiry - (await currentTime())) / 2) await advanceTimeToForNextBlock(fillTimestamp) await marketplace.fillSlot(slot.request, slot.index, proof) await waitUntilCancelled(request) - const expectedPartialHostPayout = (request.expiry - fillTimestamp) * request.ask.reward + const expectedPartialHostPayout = + (request.expiry - fillTimestamp) * request.ask.reward switchAccount(client) await marketplace.withdrawFunds(slot.request) const endBalance = await token.balanceOf(client.address) - expect(ACCOUNT_STARTING_BALANCE - endBalance).to.equal(expectedPartialHostPayout) + expect(ACCOUNT_STARTING_BALANCE - endBalance).to.equal( + expectedPartialHostPayout + ) }) }) @@ -872,7 +883,7 @@ describe("Marketplace", function () { for (let i = 0; i < slashCriterion; i++) { await waitUntilProofIsRequired(id) let missedPeriod = periodOf(await currentTime()) - await advanceTimeForNextBlock(period+1) + await advanceTimeForNextBlock(period + 1) await marketplace.markProofAsMissing(id, missedPeriod) } const expectedBalance = @@ -900,7 +911,7 @@ describe("Marketplace", function () { ) await waitUntilProofIsRequired(slotId(slot)) const missedPeriod = periodOf(await currentTime()) - await advanceTimeForNextBlock(period+1) + await advanceTimeForNextBlock(period + 1) await marketplace.markProofAsMissing(slotId(slot), missedPeriod) } expect(await marketplace.slotState(slotId(slot))).to.equal(SlotState.Free) @@ -924,7 +935,7 @@ describe("Marketplace", function () { ) await waitUntilProofIsRequired(slotId(slot)) const missedPeriod = periodOf(await currentTime()) - await advanceTimeForNextBlock(period+1) + await advanceTimeForNextBlock(period + 1) expect(await marketplace.missingProofs(slotId(slot))).to.equal( missedProofs ) diff --git a/test/Proofs.test.js b/test/Proofs.test.js index d5b85d1..6e7a758 100644 --- a/test/Proofs.test.js +++ b/test/Proofs.test.js @@ -29,9 +29,14 @@ describe("Proofs", function () { await snapshot() await ensureMinimumBlockHeight(256) const Proofs = await ethers.getContractFactory("TestProofs") - const Verifier = await ethers.getContractFactory("contracts/verifiers/testing/verifier.sol:Groth16Verifier") + const Verifier = await ethers.getContractFactory( + "contracts/verifiers/testing/verifier.sol:Groth16Verifier" + ) const verifier = await Verifier.deploy() - proofs = await Proofs.deploy({ period, timeout, downtime }, verifier.address) + proofs = await Proofs.deploy( + { period, timeout, downtime }, + verifier.address + ) }) afterEach(async function () { @@ -155,7 +160,7 @@ describe("Proofs", function () { }) describe("when proofs are required", function () { - const proof = loadProof('testing') + const proof = loadProof("testing") beforeEach(async function () { await proofs.setSlotState(slotId, SlotState.Filled) @@ -199,16 +204,42 @@ describe("Proofs", function () { }) it("fails proof submission when proof is incorrect", async function () { - await expect(proofs.submitProof(slotId, ["0x1bcdb9a3c52070f56e8d59b29239f0528817f99745157ce4d03faefddfff6acc", "0x2496ab7dd8f0596c21653105e4af7e48eb5395ea45e0c876d7db4dd31b4df23e"],[["0x002ef03c350ccfbf234bfde498378709edea3a506383d492b58c4c35ffecc508", "0x174d475745707d35989001e9216201bdb828130b0e78dbf772c4795fa845b5eb"],["0x1f04519f202fac14311c65d827f65f787dbe01985044278292723b9ee77ce5ee", "0x1c42f4d640e94c28401392031e74426ae68145f4f520cd576ca5e5b9af97c0bb"]],["0x1db1e61b32db677f3927ec117569e068f62747986e4ac7f54db8f2acd17e4abc", "0x20a59e1daca2ab80199c5bca2c5a7d6de6348bd795a0dd999752cc462d851128"],["0x00000000000000000000000000000000000000000000000000000001b9b78422","0x2389b3770d31a09a71cda2cb2114c203172eac63b61f76cb9f81db7adbe8fc9d","0x0000000000000000000000000000000000000000000000000000000000000003"])) - .to.be.revertedWith( - "Invalid proof" - ) + await expect( + proofs.submitProof( + slotId, + [ + "0x1bcdb9a3c52070f56e8d59b29239f0528817f99745157ce4d03faefddfff6acc", + "0x2496ab7dd8f0596c21653105e4af7e48eb5395ea45e0c876d7db4dd31b4df23e", + ], + [ + [ + "0x002ef03c350ccfbf234bfde498378709edea3a506383d492b58c4c35ffecc508", + "0x174d475745707d35989001e9216201bdb828130b0e78dbf772c4795fa845b5eb", + ], + [ + "0x1f04519f202fac14311c65d827f65f787dbe01985044278292723b9ee77ce5ee", + "0x1c42f4d640e94c28401392031e74426ae68145f4f520cd576ca5e5b9af97c0bb", + ], + ], + [ + "0x1db1e61b32db677f3927ec117569e068f62747986e4ac7f54db8f2acd17e4abc", + "0x20a59e1daca2ab80199c5bca2c5a7d6de6348bd795a0dd999752cc462d851128", + ], + [ + "0x00000000000000000000000000000000000000000000000000000001b9b78422", + "0x2389b3770d31a09a71cda2cb2114c203172eac63b61f76cb9f81db7adbe8fc9d", + "0x0000000000000000000000000000000000000000000000000000000000000003", + ] + ) + ).to.be.revertedWith("Invalid proof") }) it("emits an event when proof was submitted", async function () { - await expect(proofs.submitProof(slotId, ...proof)) - .to.emit(proofs, "ProofSubmitted") - // .withArgs(slotId, proof) // TODO: Update when ProofSubmitted updated + await expect(proofs.submitProof(slotId, ...proof)).to.emit( + proofs, + "ProofSubmitted" + ) + // .withArgs(slotId, proof) // TODO: Update when ProofSubmitted updated }) it("fails proof submission when already submitted", async function () { diff --git a/test/examples.js b/test/examples.js index 167b610..c6ea8f2 100644 --- a/test/examples.js +++ b/test/examples.js @@ -32,7 +32,7 @@ const exampleRequest = async () => { }, content: { cid: "zb2rhheVmk3bLks5MgzTqyznLu1zqGH5jrfTA1eAZXrjx7Vob", - merkleRoot: Array.from(randomBytes(32)) + merkleRoot: Array.from(randomBytes(32)), }, expiry: now + hours(1), nonce: hexlify(randomBytes(32)), diff --git a/test/marketplace.js b/test/marketplace.js index 8cae999..e3a9168 100644 --- a/test/marketplace.js +++ b/test/marketplace.js @@ -1,13 +1,13 @@ const { advanceTimeToForNextBlock, currentTime } = require("./evm") const { slotId, requestId } = require("./ids") -const {price} = require("./price"); +const { price } = require("./price") async function waitUntilCancelled(request) { await advanceTimeToForNextBlock(request.expiry + 1) } async function waitUntilStarted(contract, request, proof, token) { - await token.approve(contract.address, price(request)*request.ask.slots) + await token.approve(contract.address, price(request) * request.ask.slots) for (let i = 0; i < request.ask.slots; i++) { await contract.fillSlot(requestId(request), i, proof) diff --git a/test/proof.js b/test/proof.js index 6af720d..3118648 100644 --- a/test/proof.js +++ b/test/proof.js @@ -1,20 +1,48 @@ -const fs = require('fs') +const fs = require("fs") -const BASE_PATH = __dirname + '/../contracts/verifiers' -const PROOF_FILE_NAME = 'proof.json' -const PUBLIC_FILE_NAME = 'public.json' +const BASE_PATH = __dirname + "/../contracts/verifiers" +const PROOF_FILE_NAME = "proof.json" +const PUBLIC_FILE_NAME = "public.json" // TODO: Some error handling, when file don't exists or don't have expected format? -function loadProof (name) { - const proof = JSON.parse(fs.readFileSync(`${BASE_PATH}/${name}/${PROOF_FILE_NAME}`)) - const publicSignals = JSON.parse(fs.readFileSync(`${BASE_PATH}/${name}/${PUBLIC_FILE_NAME}`)) +function loadProof(name) { + const proof = JSON.parse( + fs.readFileSync(`${BASE_PATH}/${name}/${PROOF_FILE_NAME}`) + ) + const publicSignals = JSON.parse( + fs.readFileSync(`${BASE_PATH}/${name}/${PUBLIC_FILE_NAME}`) + ) // TODO: We need to do some input processing from the given files, that I did not have time to look into // instead I hardcoded the values. Look into https://github.com/iden3/snarkjs#26-simulate-a-verification-call // how to obtain it. //return [proof.pi_a, proof.pi_b, proof.pi_c, public] - return [["0x1bcdb9a3c52070f56e8d49b29239f0528817f99745157ce4d03faefddfff6acc", "0x2496ab7dd8f0596c21653105e4af7e48eb5395ea45e0c876d7db4dd31b4df23e"],[["0x002ef03c350ccfbf234bfde498378709edea3a506383d492b58c4c35ffecc508", "0x174d475745707d35989001e9216201bdb828130b0e78dbf772c4795fa845b5eb"],["0x1f04519f202fac14311c65d827f65f787dbe01985044278292723b9ee77ce5ee", "0x1c42f4d640e94c28401392031e74426ae68145f4f520cd576ca5e5b9af97c0bb"]],["0x1db1e61b32db677f3927ec117569e068f62747986e4ac7f54db8f2acd17e4abc", "0x20a59e1daca2ab80199c5bca2c5a7d6de6348bd795a0dd999752cc462d851128"],["0x00000000000000000000000000000000000000000000000000000001b9b78422","0x2389b3770d31a09a71cda2cb2114c203172eac63b61f76cb9f81db7adbe8fc9d","0x0000000000000000000000000000000000000000000000000000000000000003"]] + return [ + [ + "0x1bcdb9a3c52070f56e8d49b29239f0528817f99745157ce4d03faefddfff6acc", + "0x2496ab7dd8f0596c21653105e4af7e48eb5395ea45e0c876d7db4dd31b4df23e", + ], + [ + [ + "0x002ef03c350ccfbf234bfde498378709edea3a506383d492b58c4c35ffecc508", + "0x174d475745707d35989001e9216201bdb828130b0e78dbf772c4795fa845b5eb", + ], + [ + "0x1f04519f202fac14311c65d827f65f787dbe01985044278292723b9ee77ce5ee", + "0x1c42f4d640e94c28401392031e74426ae68145f4f520cd576ca5e5b9af97c0bb", + ], + ], + [ + "0x1db1e61b32db677f3927ec117569e068f62747986e4ac7f54db8f2acd17e4abc", + "0x20a59e1daca2ab80199c5bca2c5a7d6de6348bd795a0dd999752cc462d851128", + ], + [ + "0x00000000000000000000000000000000000000000000000000000001b9b78422", + "0x2389b3770d31a09a71cda2cb2114c203172eac63b61f76cb9f81db7adbe8fc9d", + "0x0000000000000000000000000000000000000000000000000000000000000003", + ], + ] } module.exports = { loadProof }