Use configSync to avoid async pragma in onStorageRequested

This commit is contained in:
Arnaud 2025-01-31 17:17:28 +01:00
parent 68565a16a5
commit 43f6a9ec0d
No known key found for this signature in database
GPG Key ID: 69D6CE281FCAE663
7 changed files with 132 additions and 73 deletions

View File

@ -51,6 +51,9 @@ proc config(market: OnChainMarket): Future[MarketplaceConfig] {.async.} =
return resolvedConfig
proc configSync(market: OnChainMarket): ?MarketplaceConfig =
return market.configuration
proc approveFunds(market: OnChainMarket, amount: UInt256) {.async.} =
debug "Approving tokens", amount
convertEthersError:
@ -79,7 +82,7 @@ method proofTimeout*(market: OnChainMarket): Future[UInt256] {.async.} =
method repairRewardPercentage*(market: OnChainMarket): Future[uint8] {.async.} =
convertEthersError:
let config = await market.contract.configuration()
let config = await market.config()
return config.collateral.repairRewardPercentage
method proofDowntime*(market: OnChainMarket): Future[uint8] {.async.} =
@ -111,12 +114,14 @@ method requestStorage(market: OnChainMarket, request: StorageRequest) {.async.}
method getRequest*(
market: OnChainMarket, id: RequestId
): Future[?StorageRequest] {.async.} =
convertEthersError:
try:
return some await market.contract.getRequest(id)
except Marketplace_UnknownRequest:
return none StorageRequest
): Future[?StorageRequest] {.async: (raises: []).} =
try:
return some await market.contract.getRequest(id)
except Marketplace_UnknownRequest:
return none StorageRequest
except CatchableError as err:
warn "Cannot retrieve the request", error = err.msg
return none StorageRequest
method requestState*(
market: OnChainMarket, requestId: RequestId
@ -128,7 +133,9 @@ method requestState*(
except Marketplace_UnknownRequest:
return none RequestState
method slotState*(market: OnChainMarket, slotId: SlotId): Future[SlotState] {.async.} =
method slotState*(
market: OnChainMarket, slotId: SlotId
): Future[SlotState] {.async: (raises: [CatchableError]).} =
convertEthersError:
let overrides = CallOverrides(blockTag: some BlockTag.pending)
return await market.contract.slotState(slotId, overrides)
@ -486,22 +493,32 @@ method queryPastStorageRequestedEvents*(
method slotCollateral*(
market: OnChainMarket, requestId: RequestId, slotIndex: UInt256
): Future[UInt256] {.async.} =
): Future[?UInt256] {.async: (raises: []).} =
let slotid = slotId(requestId, slotIndex)
let slotState = await market.slotState(slotid)
return await market.slotCollateral(requestId, slotState)
try:
let slotState = await market.slotState(slotid)
without request =? await market.getRequest(requestId):
return UInt256.none
return market.slotCollateral(request.ask.collateralPerSlot, slotState)
except CatchableError as err:
error "Cannot retrieve the slot state", error = err.msg
return UInt256.none
method slotCollateral*(
market: OnChainMarket, requestId: RequestId, slotState: SlotState
): Future[UInt256] {.async: (raises: [CancelledError, MarketError]).} =
without request =? await market.getRequest(requestId):
raiseMarketError("Cannot retrieve the request.")
market: OnChainMarket, collateralPerSlot: UInt256, slotState: SlotState
): ?UInt256 {.raises: [].} =
if slotState == SlotState.Repair:
let repairRewardPercentage = (await market.repairRewardPercentage).u256
return
request.ask.collateralPerSlot -
(request.ask.collateralPerSlot * repairRewardPercentage).div(100.u256)
without repairRewardPercentage =?
market.configSync .? collateral .? repairRewardPercentage:
return UInt256.none
return request.ask.collateralPerSlot
return (
collateralPerSlot - (collateralPerSlot * repairRewardPercentage.u256).div(
100.u256
)
).some
return collateralPerSlot.some

View File

@ -100,7 +100,7 @@ method mySlots*(market: Market): Future[seq[SlotId]] {.base, async.} =
method getRequest*(
market: Market, id: RequestId
): Future[?StorageRequest] {.base, async.} =
): Future[?StorageRequest] {.base, async: (raises: []).} =
raiseAssert("not implemented")
method requestState*(
@ -108,7 +108,9 @@ method requestState*(
): Future[?RequestState] {.base, async.} =
raiseAssert("not implemented")
method slotState*(market: Market, slotId: SlotId): Future[SlotState] {.base, async.} =
method slotState*(
market: Market, slotId: SlotId
): Future[SlotState] {.base, async: (raises: [CatchableError]).} =
raiseAssert("not implemented")
method getRequestEnd*(
@ -271,10 +273,10 @@ method queryPastStorageRequestedEvents*(
method slotCollateral*(
market: Market, requestId: RequestId, slotIndex: UInt256
): Future[UInt256] {.base, async.} =
): Future[?UInt256] {.base, async: (raises: []).} =
raiseAssert("not implemented")
method slotCollateral*(
market: Market, requestId: RequestId, slotState: SlotState
): Future[UInt256] {.base, async: (raises: [CancelledError, MarketError]).} =
market: Market, collateralPerSlot: UInt256, slotState: SlotState
): ?UInt256 {.base, gcsafe, raises: [].} =
raiseAssert("not implemented")

View File

@ -153,8 +153,10 @@ proc cleanUp(
# Re-add items back into the queue to prevent small availabilities from
# draining the queue. Seen items will be ordered last.
if reprocessSlot and request =? data.request:
let collateral =
await sales.context.market.slotCollateral(data.requestId, data.slotIndex)
without collateral =?
await sales.context.market.slotCollateral(data.requestId, data.slotIndex):
error "Unable to calculate collateral; configuration data may not be retrievable."
return
let queue = sales.context.slotQueue
var seenItem = SlotQueueItem.init(
@ -288,7 +290,7 @@ proc onAvailabilityAdded(sales: Sales, availability: Availability) {.async.} =
proc onStorageRequested(
sales: Sales, requestId: RequestId, ask: StorageAsk, expiry: UInt256
) {.async.} =
) {.raises: [].} =
logScope:
topics = "marketplace sales onStorageRequested"
requestId
@ -300,7 +302,10 @@ proc onStorageRequested(
trace "storage requested, adding slots to queue"
let market = sales.context.market
let collateral = await market.slotCollateral(requestId, SlotState.Free)
without collateral =? market.slotCollateral(ask.collateralPerSlot, SlotState.Free):
error "Unable to calculate collateral; configuration data may not be retrievable."
return
without items =? SlotQueueItem.init(requestId, ask, expiry, collateral).catch, err:
if err of SlotsOutOfRangeError:
@ -331,31 +336,39 @@ proc onSlotFreed(sales: Sales, requestId: RequestId, slotIndex: UInt256) =
let context = sales.context
let market = context.market
let queue = context.slotQueue
var slotQueueItem: SlotQueueItem
try:
without request =? await market.getRequest(requestId):
error "unknown request in contract"
return
without request =? (await market.getRequest(requestId)), err:
error "unknown request in contract", error = err.msgDetail
return
# Take the repairing state into consideration to calculate the collateral.
# This is particularly needed because it will affect the priority in the queue
# and we want to give the user the ability to tweak the parameters.
# Adding the repairing state directly in the queue priority calculation
# would not allow this flexibility.
let collateral = await market.slotCollateral(request.id, SlotState.Repair)
# Take the repairing state into consideration to calculate the collateral.
# This is particularly needed because it will affect the priority in the queue
# and we want to give the user the ability to tweak the parameters.
# Adding the repairing state directly in the queue priority calculation
# would not allow this flexibility.
without collateral =?
market.slotCollateral(request.ask.collateralPerSlot, SlotState.Repair):
error "Unable to calculate collateral; configuration data may not be retrievable."
return
slotQueueItem =
SlotQueueItem.init(request, slotIndex.truncate(uint16), collateral = collateral)
without slotQueueItem =?
SlotQueueItem.init(request, slotIndex.truncate(uint16), collateral = collateral).catch,
err:
warn "Too many slots, cannot add to queue", error = err.msgDetail
return
if err =? queue.push(slotQueueItem).errorOption:
error "failed to push slot items to queue", error = err.msgDetail
except CancelledError:
discard # do not propagate as addSlotToQueue was asyncSpawned
except CatchableError as e:
error "failed to get request from contract and add slots to queue",
error = e.msgDetail
if err =? queue.push(slotQueueItem).errorOption:
if err of SlotQueueItemExistsError:
error "Failed to push item to queue becaue it already exists",
error = err.msgDetail
elif err of QueueNotRunningError:
warn "Failed to push item to queue becaue queue is not running",
error = err.msgDetail
# We could get rid of this by adding the storage ask in the SlotFreed event,
# so we would need to call getRequest to get the collateralPerSlot.
# Or when the request cache is merged, we could assume that the request will be
# in the cache.
let fut = addSlotToQueue()
sales.trackedFutures.track(fut)
asyncSpawn fut
@ -364,8 +377,13 @@ proc subscribeRequested(sales: Sales) {.async.} =
let context = sales.context
let market = context.market
proc onStorageRequested(requestId: RequestId, ask: StorageAsk, expiry: UInt256) =
discard sales.onStorageRequested(requestId, ask, expiry)
proc onStorageRequested(
requestId: RequestId, ask: StorageAsk, expiry: UInt256
) {.raises: [].} =
sales.onStorageRequested(requestId, ask, expiry)
# Ensure that the config is loaded and repairRewardPercentage is available
discard await market.repairRewardPercentage()
try:
let sub = await market.subscribeRequests(onStorageRequested)

View File

@ -161,7 +161,7 @@ proc init*(
ask: StorageAsk,
expiry: UInt256,
collateral: UInt256,
): seq[SlotQueueItem] =
): seq[SlotQueueItem] {.raises: [SlotsOutOfRangeError].} =
if not ask.slots.inRange:
raise newException(SlotsOutOfRangeError, "Too many slots")
@ -240,7 +240,7 @@ proc unpause*(self: SlotQueue) =
# set unpaused flag to true -- unblocks coroutines waiting on unpaused.wait()
self.unpaused.fire()
proc push*(self: SlotQueue, item: SlotQueueItem): ?!void =
proc push*(self: SlotQueue, item: SlotQueueItem): ?!void {.raises: [].} =
logScope:
requestId = item.requestId
slotIndex = item.slotIndex

View File

@ -35,7 +35,9 @@ method run(state: SaleFilling, machine: Machine): Future[?State] {.async.} =
requestId = data.requestId
slotIndex = data.slotIndex
let collateral = await market.slotCollateral(data.requestId, data.slotIndex)
without collateral =? await market.slotCollateral(data.requestId, data.slotIndex):
error "Unable to calculate collateral; configuration data may not be retrievable."
return
debug "Filling slot"
try:

View File

@ -165,7 +165,7 @@ method mySlots*(market: MockMarket): Future[seq[SlotId]] {.async.} =
method getRequest*(
market: MockMarket, id: RequestId
): Future[?StorageRequest] {.async.} =
): Future[?StorageRequest] {.async: (raises: []).} =
for request in market.requested:
if request.id == id:
return some request
@ -183,10 +183,18 @@ method requestState*(
): Future[?RequestState] {.async.} =
return market.requestState .? [requestId]
method slotState*(market: MockMarket, slotId: SlotId): Future[SlotState] {.async.} =
if not market.slotState.hasKey(slotId):
method slotState*(
market: MockMarket, slotId: SlotId
): Future[SlotState] {.async: (raises: [CatchableError]).} =
if slotId notin market.slotState:
return SlotState.Free
return market.slotState[slotId]
try:
return market.slotState[slotId]
except ref KeyError:
# Should never reach that case.
# Just returning a random slot state.
return SlotState.Repair
method getRequestEnd*(
market: MockMarket, id: RequestId
@ -529,21 +537,27 @@ method unsubscribe*(subscription: SlotReservationsFullSubscription) {.async.} =
method slotCollateral*(
market: MockMarket, requestId: RequestId, slotIndex: UInt256
): Future[UInt256] {.async.} =
): Future[?UInt256] {.async: (raises: []).} =
let slotid = slotId(requestId, slotIndex)
let state = await slotState(market, slotid)
return await market.slotCollateral(requestId, state)
try:
let state = await slotState(market, slotid)
without request =? await market.getRequest(requestId):
return UInt256.none
return market.slotCollateral(request.ask.collateralPerSlot, state)
except CatchableError:
return UInt256.none
method slotCollateral*(
market: MockMarket, requestId: RequestId, slotState: SlotState
): Future[UInt256] {.async: (raises: [CancelledError, MarketError]).} =
without request =? await market.getRequest(requestId):
raiseMarketError("Cannot retrieve the request.")
market: MockMarket, collateralPerSlot: UInt256, slotState: SlotState
): ?UInt256 {.raises: [].} =
if slotState == SlotState.Repair:
let repairRewardPercentage = market.config.collateral.repairRewardPercentage.u256
return
request.ask.collateralPerSlot -
(request.ask.collateralPerSlot * repairRewardPercentage).div(100.u256)
return request.ask.collateralPerSlot
return (
collateralPerSlot - (collateralPerSlot * repairRewardPercentage).div(100.u256)
).some
return collateralPerSlot.some

View File

@ -598,18 +598,24 @@ ethersuite "On-Chain Market":
await market.fillSlot(request.id, 0.u256, proof, request.ask.collateralPerSlot)
let slotId = request.slotId(0.u256)
let collateral = await market.slotCollateral(request.id, 0.u256)
without collateral =? await market.slotCollateral(request.id, 0.u256):
fail()
check collateral == request.ask.collateralPerSlot
test "calculates correctly the collateral when the slot is being repaired":
# Ensure that the config is loaded and repairRewardPercentage is available
discard await market.repairRewardPercentage()
await market.requestStorage(request)
await market.reserveSlot(request.id, 0.u256)
await market.fillSlot(request.id, 0.u256, proof, request.ask.collateralPerSlot)
await market.freeSlot(slotId(request.id, 0.u256))
let slotId = request.slotId(0.u256)
let collateral = await market.slotCollateral(request.id, 0.u256)
without collateral =? await market.slotCollateral(request.id, 0.u256):
fail()
# slotCollateral
# repairRewardPercentage = 10