feat: partial rewards and withdraws (#880)
* feat: partial rewards and withdraws * test: missing reserve slot * test: fix contracts
This commit is contained in:
parent
b0607d3fdb
commit
eff0d8cd18
|
@ -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))
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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":
|
||||
|
|
|
@ -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..<request.ask.slots:
|
||||
for slotIndex in 1..<request.ask.slots:
|
||||
await market.reserveSlot(request.id, slotIndex.u256)
|
||||
await market.fillSlot(request.id, slotIndex.u256, proof, request.ask.collateral)
|
||||
|
||||
|
@ -456,13 +464,11 @@ ethersuite "On-Chain Market":
|
|||
await ethProvider.advanceTimeTo(requestEnd.u256 + 1)
|
||||
|
||||
let startBalance = await token.balanceOf(address)
|
||||
|
||||
await market.freeSlot(request.slotId(0.u256))
|
||||
|
||||
let endBalance = await token.balanceOf(address)
|
||||
check endBalance == (startBalance +
|
||||
request.ask.duration * request.ask.reward +
|
||||
request.ask.collateral)
|
||||
|
||||
let expectedPayout = request.expectedPayout(filledAt, requestEnd.u256)
|
||||
check endBalance == (startBalance + expectedPayout + request.ask.collateral)
|
||||
|
||||
test "pays rewards to reward recipient, collateral to host":
|
||||
market = OnChainMarket.new(marketplace, hostRewardRecipient.some)
|
||||
|
@ -471,7 +477,11 @@ ethersuite "On-Chain Market":
|
|||
await market.requestStorage(request)
|
||||
|
||||
switchAccount(host)
|
||||
for slotIndex in 0..<request.ask.slots:
|
||||
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 1..<request.ask.slots:
|
||||
await market.reserveSlot(request.id, slotIndex.u256)
|
||||
await market.fillSlot(request.id, slotIndex.u256, proof, request.ask.collateral)
|
||||
|
||||
|
@ -486,6 +496,6 @@ ethersuite "On-Chain Market":
|
|||
let endBalanceHost = await token.balanceOf(hostAddress)
|
||||
let endBalanceReward = await token.balanceOf(hostRewardRecipient)
|
||||
|
||||
let expectedPayout = request.expectedPayout(filledAt, requestEnd.u256)
|
||||
check endBalanceHost == (startBalanceHost + request.ask.collateral)
|
||||
check endBalanceReward == (startBalanceReward +
|
||||
request.ask.duration * request.ask.reward)
|
||||
check endBalanceReward == (startBalanceReward + expectedPayout)
|
||||
|
|
|
@ -44,7 +44,7 @@ twonodessuite "Marketplace", debug1 = false, debug2 = false:
|
|||
check reservations.len == 5
|
||||
check reservations[0].requestId == purchase.requestId
|
||||
|
||||
test "node slots gets paid out":
|
||||
test "node slots gets paid out and rest of tokens are returned to client":
|
||||
let size = 0xFFFFFF.u256
|
||||
let data = await RandomChunker.example(blocks = 8)
|
||||
let marketplace = Marketplace.new(Marketplace.address, ethProvider.getSigner())
|
||||
|
@ -55,7 +55,7 @@ twonodessuite "Marketplace", debug1 = false, debug2 = false:
|
|||
let nodes = 5'u
|
||||
|
||||
# client 2 makes storage available
|
||||
let startBalance = await token.balanceOf(account2)
|
||||
let startBalanceHost = await token.balanceOf(account2)
|
||||
discard client2.postAvailability(totalSize=size, duration=20*60.u256, minPrice=300.u256, maxCollateral=300.u256).get
|
||||
|
||||
# client 1 requests storage
|
||||
|
@ -74,12 +74,18 @@ twonodessuite "Marketplace", debug1 = false, debug2 = false:
|
|||
let purchase = client1.getPurchase(id).get
|
||||
check purchase.error == none string
|
||||
|
||||
let clientBalanceBeforeFinished = await token.balanceOf(account1)
|
||||
|
||||
# Proving mechanism uses blockchain clock to do proving/collect/cleanup round
|
||||
# hence we must use `advanceTime` over `sleepAsync` as Hardhat does mine new blocks
|
||||
# only with new transaction
|
||||
await ethProvider.advanceTime(duration)
|
||||
|
||||
check eventually (await token.balanceOf(account2)) - startBalance == duration*reward*nodes.u256
|
||||
# Checking that the hosting node received reward for at least the time between <expiry;end>
|
||||
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":
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit f5a54c7ed4e9d562d984b02169a7e6ab2be973ba
|
||||
Subproject commit 997696a20e0976011cdbc2f0ff3a844672056ba2
|
Loading…
Reference in New Issue