import std/sequtils import pkg/questionable import pkg/upraises import pkg/stint import pkg/nimcrypto import pkg/chronicles import ./rng import ./market import ./clock import ./proving import ./contracts/requests import ./sales/salescontext import ./sales/salesagent import ./sales/availability import ./sales/statemachine import ./sales/states/downloading import ./sales/states/unknown ## Sales holds a list of available storage that it may sell. ## ## When storage is requested on the market that matches availability, the Sales ## object will instruct the Codex node to persist the requested data. Once the ## data has been persisted, it uploads a proof of storage to the market in an ## attempt to win a storage contract. ## ## Node Sales Market ## | | | ## | -- add availability --> | | ## | | <-- storage request --- | ## | <----- store data ------ | | ## | -----------------------> | | ## | | | ## | <----- prove data ---- | | ## | -----------------------> | | ## | | ---- storage proof ---> | export stint export availability type Sales* = ref object context*: SalesContext subscription*: ?market.Subscription available: seq[Availability] agents*: seq[SalesAgent] proc `onStore=`*(sales: Sales, onStore: OnStore) = sales.context.onStore = some onStore proc `onProve=`*(sales: Sales, onProve: OnProve) = sales.context.onProve = some onProve proc `onClear=`*(sales: Sales, onClear: OnClear) = sales.context.onClear = some onClear proc `onSale=`*(sales: Sales, callback: OnSale) = sales.context.onSale = some callback proc onStore*(sales: Sales): ?OnStore = sales.context.onStore proc onProve*(sales: Sales): ?OnProve = sales.context.onProve proc onClear*(sales: Sales): ?OnClear = sales.context.onClear proc onSale*(sales: Sales): ?OnSale = sales.context.onSale proc available*(sales: Sales): seq[Availability] = sales.available proc init*(_: type Availability, size: UInt256, duration: UInt256, minPrice: UInt256): Availability = var id: array[32, byte] doAssert randomBytes(id) == 32 Availability(id: id, size: size, duration: duration, minPrice: minPrice) 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 new*(_: type Sales, market: Market, clock: Clock, proving: Proving): Sales = let sales = Sales(context: SalesContext( market: market, clock: clock, proving: proving )) proc onSaleErrored(availability: Availability) = sales.add(availability) sales.context.onSaleErrored = some onSaleErrored sales 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 proc randomSlotIndex(numSlots: uint64): UInt256 = let rng = Rng.instance let slotIndex = rng.rand(numSlots - 1) return slotIndex.u256 proc findSlotIndex(numSlots: uint64, requestId: RequestId, slotId: SlotId): ?UInt256 = for i in 0..