nim-codex/dagger/sales.nim

132 lines
4.0 KiB
Nim
Raw Normal View History

2022-03-30 15:28:56 +00:00
import std/times
2022-03-30 10:51:28 +00:00
import std/sequtils
import pkg/questionable
import pkg/upraises
import pkg/stint
import pkg/nimcrypto
import ./market
export stint
2022-03-30 15:28:56 +00:00
const DefaultOfferExpiryInterval = (10 * 60).u256
2022-03-30 10:51:28 +00:00
type
Sales* = ref object
market: Market
subscription: ?Subscription
available*: seq[Availability]
2022-03-30 15:28:56 +00:00
offerExpiryInterval*: UInt256
2022-03-31 09:41:45 +00:00
onSale: ?OnSale
2022-03-30 10:51:28 +00:00
Availability* = object
id*: array[32, byte]
size*: uint64
duration*: uint64
minPrice*: UInt256
2022-03-31 12:35:53 +00:00
Negotiation = ref object
sales: Sales
request: StorageRequest
availability: Availability
offer: ?StorageOffer
subscription: ?Subscription
waiting: ?Future[void]
OnSale = proc(offer: StorageOffer) {.gcsafe, upraises: [].}
2022-03-30 10:51:28 +00:00
func new*(_: type Sales, market: Market): Sales =
2022-03-30 15:28:56 +00:00
Sales(market: market, offerExpiryInterval: DefaultOfferExpiryInterval)
2022-03-30 10:51:28 +00:00
proc init*(_: type Availability,
2022-03-30 10:51:28 +00:00
size: uint64,
duration: uint64,
minPrice: UInt256): Availability =
var id: array[32, byte]
doAssert randomBytes(id) == 32
Availability(id: id, size: size, duration: duration, minPrice: minPrice)
2022-03-31 09:41:45 +00:00
proc `onSale=`*(sales: Sales, callback: OnSale) =
sales.onSale = some callback
2022-03-30 10:51:28 +00:00
func add*(sales: Sales, availability: Availability) =
sales.available.add(availability)
func remove*(sales: Sales, availability: Availability) =
sales.available.keepItIf(it != availability)
func findAvailability(sales: Sales, request: StorageRequest): ?Availability =
for availability in sales.available:
if request.size <= availability.size.u256 and
request.duration <= availability.duration.u256 and
request.maxPrice >= availability.minPrice:
return some availability
2022-03-31 12:35:53 +00:00
proc createOffer(negotiation: Negotiation): StorageOffer =
2022-03-30 10:51:28 +00:00
StorageOffer(
2022-03-31 12:35:53 +00:00
requestId: negotiation.request.id,
price: negotiation.request.maxPrice,
expiry: getTime().toUnix().u256 + negotiation.sales.offerExpiryInterval
2022-03-30 10:51:28 +00:00
)
2022-03-31 12:35:53 +00:00
proc sendOffer(negotiation: Negotiation) {.async.} =
let offer = negotiation.createOffer()
negotiation.offer = some await negotiation.sales.market.offerStorage(offer)
proc onSelect(negotiation: Negotiation, offerId: array[32, byte]) =
if subscription =? negotiation.subscription:
asyncSpawn subscription.unsubscribe()
without offer =? negotiation.offer:
return
if offer.id == offerId:
if onSale =? negotiation.sales.onSale:
onSale(offer)
else:
negotiation.sales.add(negotiation.availability)
proc subscribeSelect(negotiation: Negotiation) {.async.} =
without offer =? negotiation.offer:
return
2022-03-31 12:35:53 +00:00
proc onSelect(offerId: array[32, byte]) {.gcsafe, upraises:[].} =
negotiation.onSelect(offerId)
let market = negotiation.sales.market
let subscription = await market.subscribeSelection(offer.requestId, onSelect)
negotiation.subscription = some subscription
2022-03-31 12:35:53 +00:00
proc waitForExpiry(negotiation: Negotiation) {.async.} =
without offer =? negotiation.offer:
return
await negotiation.sales.market.waitUntil(offer.expiry)
proc start(negotiation: Negotiation) {.async.} =
let sales = negotiation.sales
let availability = negotiation.availability
sales.remove(availability)
2022-03-31 12:35:53 +00:00
await negotiation.sendOffer()
await negotiation.subscribeSelect()
await negotiation.waitForExpiry()
2022-03-31 12:35:53 +00:00
proc handleRequest(sales: Sales, request: StorageRequest) {.async.} =
without availability =? sales.findAvailability(request):
return
2022-03-31 12:35:53 +00:00
let negotiation = Negotiation(
sales: sales,
request: request,
availability: availability
)
asyncSpawn negotiation.start()
2022-03-30 10:51:28 +00:00
proc start*(sales: Sales) =
doAssert sales.subscription.isNone, "Sales already started"
proc onRequest(request: StorageRequest) {.gcsafe, upraises:[].} =
asyncSpawn sales.handleRequest(request)
proc subscribe {.async.} =
sales.subscription = some await sales.market.subscribeRequests(onRequest)
asyncSpawn subscribe()
proc stop*(sales: Sales) =
if subscription =? sales.subscription:
asyncSpawn subscription.unsubscribe()
sales.subscription = Subscription.none