From ffa203b04fdb3e3f0938c25ab3db386948d11f5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Uhl=C3=AD=C5=99?= Date: Thu, 10 Oct 2024 13:53:33 +0200 Subject: [PATCH] feat: partial rewards and withdraws (#880) * feat: partial rewards and withdraws * test: missing reserve slot * test: fix contracts --- codex/purchasing/states/failed.nim | 5 +++++ codex/purchasing/states/finished.nim | 4 +++- tests/codex/helpers/mockmarket.nim | 4 +++- tests/codex/testpurchasing.nim | 18 +++++++++++++++++ tests/contracts/testContracts.nim | 10 +++++++--- tests/contracts/testMarket.nim | 28 ++++++++++++++++++--------- tests/integration/testmarketplace.nim | 12 +++++++++--- vendor/codex-contracts-eth | 2 +- 8 files changed, 65 insertions(+), 18 deletions(-) diff --git a/codex/purchasing/states/failed.nim b/codex/purchasing/states/failed.nim index 1ade3e4c..b05dbb6f 100644 --- a/codex/purchasing/states/failed.nim +++ b/codex/purchasing/states/failed.nim @@ -1,5 +1,6 @@ import pkg/metrics import ../statemachine +import ../../logutils import ./error declareCounter(codex_purchases_failed, "codex purchases failed") @@ -12,5 +13,9 @@ method `$`*(state: PurchaseFailed): string = method run*(state: PurchaseFailed, machine: Machine): Future[?State] {.async.} = codex_purchases_failed.inc() + let purchase = Purchase(machine) + warn "Request failed, withdrawing remaining funds", requestId = purchase.requestId + await purchase.market.withdrawFunds(purchase.requestId) + let error = newException(PurchaseError, "Purchase failed") return some State(PurchaseErrored(error: error)) diff --git a/codex/purchasing/states/finished.nim b/codex/purchasing/states/finished.nim index 0f97150d..6cf5ffcc 100644 --- a/codex/purchasing/states/finished.nim +++ b/codex/purchasing/states/finished.nim @@ -16,5 +16,7 @@ method `$`*(state: PurchaseFinished): string = method run*(state: PurchaseFinished, machine: Machine): Future[?State] {.async.} = codex_purchases_finished.inc() let purchase = Purchase(machine) - info "Purchase finished", requestId = purchase.requestId + info "Purchase finished, withdrawing remaining funds", requestId = purchase.requestId + await purchase.market.withdrawFunds(purchase.requestId) + purchase.future.complete() diff --git a/tests/codex/helpers/mockmarket.nim b/tests/codex/helpers/mockmarket.nim index 497fa046..d19f0c21 100644 --- a/tests/codex/helpers/mockmarket.nim +++ b/tests/codex/helpers/mockmarket.nim @@ -269,7 +269,9 @@ method freeSlot*(market: MockMarket, slotId: SlotId) {.async.} = method withdrawFunds*(market: MockMarket, requestId: RequestId) {.async.} = market.withdrawn.add(requestId) - market.emitRequestCancelled(requestId) + + if state =? market.requestState.?[requestId] and state == RequestState.Cancelled: + market.emitRequestCancelled(requestId) proc setProofRequired*(mock: MockMarket, id: SlotId, required: bool) = if required: diff --git a/tests/codex/testpurchasing.nim b/tests/codex/testpurchasing.nim index 25504732..85c6e114 100644 --- a/tests/codex/testpurchasing.nim +++ b/tests/codex/testpurchasing.nim @@ -230,3 +230,21 @@ checksuite "Purchasing state machine": let next = await future check !next of PurchaseFinished + + test "withdraw funds in PurchaseFinished": + let request = StorageRequest.example + let purchase = Purchase.new(request, market, clock) + discard await PurchaseFinished().run(purchase) + check request.id in market.withdrawn + + test "withdraw funds in PurchaseFailed": + let request = StorageRequest.example + let purchase = Purchase.new(request, market, clock) + discard await PurchaseFailed().run(purchase) + check request.id in market.withdrawn + + test "withdraw funds in PurchaseCancelled": + let request = StorageRequest.example + let purchase = Purchase.new(request, market, clock) + discard await PurchaseCancelled().run(purchase) + check request.id in market.withdrawn diff --git a/tests/contracts/testContracts.nim b/tests/contracts/testContracts.nim index ba837bbd..b098b80f 100644 --- a/tests/contracts/testContracts.nim +++ b/tests/contracts/testContracts.nim @@ -17,6 +17,10 @@ ethersuite "Marketplace contracts": var periodicity: Periodicity var request: StorageRequest var slotId: SlotId + var filledAt: UInt256 + + proc expectedPayout(endTimestamp: UInt256): UInt256 = + return (endTimestamp - filledAt) * request.ask.reward proc switchAccount(account: Signer) = marketplace = marketplace.connect(account) @@ -46,6 +50,7 @@ ethersuite "Marketplace contracts": switchAccount(host) discard await token.approve(marketplace.address, request.ask.collateral) discard await marketplace.reserveSlot(request.id, 0.u256) + filledAt = await ethProvider.currentTime() discard await marketplace.fillSlot(request.id, 0.u256, proof) slotId = request.slotId(0.u256) @@ -87,8 +92,7 @@ ethersuite "Marketplace contracts": let startBalance = await token.balanceOf(address) discard await marketplace.freeSlot(slotId) let endBalance = await token.balanceOf(address) - - check endBalance == (startBalance + request.ask.duration * request.ask.reward + request.ask.collateral) + check endBalance == (startBalance + expectedPayout(requestEnd.u256) + request.ask.collateral) test "can be paid out at the end, specifying reward and collateral recipient": switchAccount(host) @@ -105,7 +109,7 @@ ethersuite "Marketplace contracts": let endBalanceCollateral = await token.balanceOf(collateralRecipient) check endBalanceHost == startBalanceHost - check endBalanceReward == (startBalanceReward + request.ask.duration * request.ask.reward) + check endBalanceReward == (startBalanceReward + expectedPayout(requestEnd.u256)) check endBalanceCollateral == (startBalanceCollateral + request.ask.collateral) test "cannot mark proofs missing for cancelled request": diff --git a/tests/contracts/testMarket.nim b/tests/contracts/testMarket.nim index 3de71da7..66088b71 100644 --- a/tests/contracts/testMarket.nim +++ b/tests/contracts/testMarket.nim @@ -20,8 +20,12 @@ ethersuite "On-Chain Market": var slotIndex: UInt256 var periodicity: Periodicity var host: Signer + var otherHost: Signer var hostRewardRecipient: Address + proc expectedPayout(r: StorageRequest, startTimestamp: UInt256, endTimestamp: UInt256): UInt256 = + return (endTimestamp - startTimestamp) * r.ask.reward + proc switchAccount(account: Signer) = marketplace = marketplace.connect(account) token = token.connect(account) @@ -42,6 +46,7 @@ ethersuite "On-Chain Market": request = StorageRequest.example request.client = accounts[0] host = ethProvider.getSigner(accounts[1]) + otherHost = ethProvider.getSigner(accounts[3]) slotIndex = (request.ask.slots div 2).u256 @@ -447,8 +452,11 @@ ethersuite "On-Chain Market": let address = await host.getAddress() switchAccount(host) + await market.reserveSlot(request.id, 0.u256) + await market.fillSlot(request.id, 0.u256, proof, request.ask.collateral) + let filledAt = (await ethProvider.currentTime()) - 1.u256 - for slotIndex in 0.. + check eventually (await token.balanceOf(account2)) - startBalanceHost >= (duration-5*60)*reward*nodes.u256 + + # Checking that client node receives some funds back that were not used for the host nodes + check eventually (await token.balanceOf(account1)) - clientBalanceBeforeFinished > 0 marketplacesuite "Marketplace payouts": diff --git a/vendor/codex-contracts-eth b/vendor/codex-contracts-eth index f5a54c7e..997696a2 160000 --- a/vendor/codex-contracts-eth +++ b/vendor/codex-contracts-eth @@ -1 +1 @@ -Subproject commit f5a54c7ed4e9d562d984b02169a7e6ab2be973ba +Subproject commit 997696a20e0976011cdbc2f0ff3a844672056ba2