nim-codex/codex/purchasing.nim

113 lines
3.3 KiB
Nim
Raw Normal View History

import std/tables
2022-03-23 12:57:48 +00:00
import pkg/stint
import pkg/chronos
import pkg/questionable
import pkg/nimcrypto
import ./market
import ./clock
2022-03-23 12:57:48 +00:00
export questionable
export market
2022-03-23 12:57:48 +00:00
type
Purchasing* = ref object
market: Market
clock: Clock
purchases: Table[array[32, byte], Purchase]
2022-03-23 12:57:48 +00:00
proofProbability*: UInt256
requestExpiryInterval*: UInt256
offerExpiryMargin*: UInt256
2022-03-23 12:57:48 +00:00
Purchase* = ref object
future: Future[void]
market: Market
clock: Clock
offerExpiryMargin: UInt256
request*: StorageRequest
offers*: seq[StorageOffer]
selected*: ?StorageOffer
2022-03-23 12:57:48 +00:00
const DefaultProofProbability = 100.u256
const DefaultRequestExpiryInterval = (10 * 60).u256
const DefaultOfferExpiryMargin = (8 * 60).u256
2022-03-23 12:57:48 +00:00
proc start(purchase: Purchase) {.gcsafe.}
func id*(purchase: Purchase): array[32, byte]
proc new*(_: type Purchasing, market: Market, clock: Clock): Purchasing =
2022-03-23 12:57:48 +00:00
Purchasing(
market: market,
clock: clock,
2022-03-23 12:57:48 +00:00
proofProbability: DefaultProofProbability,
requestExpiryInterval: DefaultRequestExpiryInterval,
offerExpiryMargin: DefaultOfferExpiryMargin
2022-03-23 12:57:48 +00:00
)
proc populate*(purchasing: Purchasing, request: StorageRequest): StorageRequest =
result = request
if result.ask.proofProbability == 0.u256:
result.ask.proofProbability = purchasing.proofProbability
if result.expiry == 0.u256:
result.expiry = (purchasing.clock.now().u256 + purchasing.requestExpiryInterval)
if result.nonce == array[32, byte].default:
doAssert randomBytes(result.nonce) == 32
proc purchase*(purchasing: Purchasing, request: StorageRequest): Purchase =
let request = purchasing.populate(request)
let purchase = Purchase(
request: request,
market: purchasing.market,
clock: purchasing.clock,
offerExpiryMargin: purchasing.offerExpiryMargin
)
purchase.start()
purchasing.purchases[purchase.id] = purchase
purchase
func getPurchase*(purchasing: Purchasing, id: array[32, byte]): ?Purchase =
if purchasing.purchases.hasKey(id):
some purchasing.purchases[id]
else:
none Purchase
2022-03-28 10:28:22 +00:00
proc selectOffer(purchase: Purchase) {.async.} =
var cheapest: ?StorageOffer
for offer in purchase.offers:
without purchase.clock.now().u256 < offer.expiry - purchase.offerExpiryMargin:
2022-03-28 12:40:41 +00:00
continue
without current =? cheapest:
cheapest = some offer
continue
if current.price > offer.price:
2022-03-28 10:28:22 +00:00
cheapest = some offer
if cheapest =? cheapest:
await purchase.market.selectOffer(cheapest.id)
purchase.selected = some cheapest
2022-03-28 10:28:22 +00:00
proc run(purchase: Purchase) {.async.} =
2022-03-28 10:28:22 +00:00
proc onOffer(offer: StorageOffer) =
purchase.offers.add(offer)
let market = purchase.market
purchase.request = await market.requestStorage(purchase.request)
let subscription = await market.subscribeOffers(purchase.request.id, onOffer)
await purchase.clock.waitUntil(purchase.request.expiry.truncate(int64))
2022-03-28 10:28:22 +00:00
await purchase.selectOffer()
await subscription.unsubscribe()
proc start(purchase: Purchase) =
purchase.future = purchase.run()
2022-03-23 12:57:48 +00:00
proc wait*(purchase: Purchase) {.async.} =
await purchase.future
func id*(purchase: Purchase): array[32, byte] =
purchase.request.id
func finished*(purchase: Purchase): bool =
purchase.future.finished
func error*(purchase: Purchase): ?(ref CatchableError) =
if purchase.future.failed:
some purchase.future.error
else:
none (ref CatchableError)