nim-codex/codex/sales/statemachine.nim

110 lines
3.8 KiB
Nim

import std/sequtils
import pkg/chronos
import pkg/questionable
import pkg/upraises
import ../errors
import ../utils/statemachine
import ../market
import ../clock
import ../proving
import ../contracts/requests
export market
export clock
export statemachine
export proving
type
Sales* = ref object
market*: Market
clock*: Clock
subscription*: ?market.Subscription
available: seq[Availability]
onStore: ?OnStore
onProve: ?OnProve
onClear: ?OnClear
onSale: ?OnSale
proving*: Proving
agents*: seq[SalesAgent]
SalesAgent* = ref object of StateMachineAsync
sales*: Sales
requestId*: RequestId
ask*: StorageAsk
availability*: ?Availability # TODO: when availability persistence is added, change this to not optional
request*: ?StorageRequest
slotIndex*: UInt256
failed*: market.Subscription
fulfilled*: market.Subscription
slotFilled*: market.Subscription
cancelled*: Future[void]
SaleState* = ref object of AsyncState
SaleError* = ref object of CodexError
Availability* = object
id*: array[32, byte]
size*: UInt256
duration*: UInt256
minPrice*: UInt256
AvailabilityChange* = proc(availability: Availability) {.gcsafe, upraises: [].}
# TODO: when availability changes introduced, make availability non-optional (if we need to keep it at all)
RequestEvent* = proc(state: SaleState, request: StorageRequest): Future[void] {.gcsafe, upraises: [].}
OnStore* = proc(request: StorageRequest,
slot: UInt256,
availability: ?Availability): Future[void] {.gcsafe, upraises: [].}
OnProve* = proc(request: StorageRequest,
slot: UInt256): Future[seq[byte]] {.gcsafe, upraises: [].}
OnClear* = proc(availability: ?Availability,# TODO: when availability changes introduced, make availability non-optional (if we need to keep it at all)
request: StorageRequest,
slotIndex: UInt256) {.gcsafe, upraises: [].}
OnSale* = proc(availability: ?Availability, # TODO: when availability changes introduced, make availability non-optional (if we need to keep it at all)
request: StorageRequest,
slotIndex: UInt256) {.gcsafe, upraises: [].}
proc `onStore=`*(sales: Sales, onStore: OnStore) =
sales.onStore = some onStore
proc `onProve=`*(sales: Sales, onProve: OnProve) =
sales.onProve = some onProve
proc `onClear=`*(sales: Sales, onClear: OnClear) =
sales.onClear = some onClear
proc `onSale=`*(sales: Sales, callback: OnSale) =
sales.onSale = some callback
proc onStore*(sales: Sales): ?OnStore = sales.onStore
proc onProve*(sales: Sales): ?OnProve = sales.onProve
proc onClear*(sales: Sales): ?OnClear = sales.onClear
proc onSale*(sales: Sales): ?OnSale = sales.onSale
proc available*(sales: Sales): seq[Availability] = sales.available
func add*(sales: Sales, availability: Availability) =
if not sales.available.contains(availability):
sales.available.add(availability)
# TODO: add to disk (persist), serialise to json.
func remove*(sales: Sales, availability: Availability) =
sales.available.keepItIf(it != availability)
# TODO: remove from disk availability, mark as in use by assigning
# a slotId, so that it can be used for restoration (node restart)
func findAvailability*(sales: Sales, ask: StorageAsk): ?Availability =
for availability in sales.available:
if ask.slotSize <= availability.size and
ask.duration <= availability.duration and
ask.pricePerSlot >= availability.minPrice:
return some availability
method onCancelled*(state: SaleState, request: StorageRequest) {.base, async.} =
discard
method onFailed*(state: SaleState, request: StorageRequest) {.base, async.} =
discard
method onSlotFilled*(state: SaleState, requestId: RequestId,
slotIndex: UInt256) {.base, async.} =
discard