[purchasing] Select cheapest offer
This commit is contained in:
parent
03140fdf49
commit
fe23cb89d7
|
@ -8,6 +8,32 @@ export offers
|
||||||
|
|
||||||
type
|
type
|
||||||
Market* = ref object of RootObj
|
Market* = ref object of RootObj
|
||||||
|
Subscription* = ref object of RootObj
|
||||||
|
OnRequest* = proc(request: StorageRequest) {.gcsafe.}
|
||||||
|
OnOffer* = proc(offer: StorageOffer) {.gcsafe.}
|
||||||
|
|
||||||
method requestStorage*(market: Market, request: StorageRequest) {.base, async.} =
|
method requestStorage*(market: Market, request: StorageRequest) {.base, async.} =
|
||||||
raiseAssert("not implemented")
|
raiseAssert("not implemented")
|
||||||
|
|
||||||
|
method offerStorage*(market: Market, offer: StorageOffer) {.base, async.} =
|
||||||
|
raiseAssert("not implemented")
|
||||||
|
|
||||||
|
method selectOffer*(market: Market, id: array[32, byte]) {.base, async.} =
|
||||||
|
raiseAssert("not implemented")
|
||||||
|
|
||||||
|
method waitUntil*(market: Market, expiry: UInt256) {.base, async.} =
|
||||||
|
raiseAssert("not implemented")
|
||||||
|
|
||||||
|
method subscribeRequests*(market: Market,
|
||||||
|
callback: OnRequest):
|
||||||
|
Future[Subscription] {.base, async.} =
|
||||||
|
raiseAssert("not implemented")
|
||||||
|
|
||||||
|
method subscribeOffers*(market: Market,
|
||||||
|
requestId: array[32, byte],
|
||||||
|
callback: OnOffer):
|
||||||
|
Future[Subscription] {.base, async.} =
|
||||||
|
raiseAssert("not implemented")
|
||||||
|
|
||||||
|
method unsubscribe*(subscription: Subscription) {.base, async.} =
|
||||||
|
raiseAssert("not implemented")
|
||||||
|
|
|
@ -47,8 +47,27 @@ proc purchase*(purchasing: Purchasing, request: StorageRequest): Purchase =
|
||||||
purchase.start()
|
purchase.start()
|
||||||
purchase
|
purchase
|
||||||
|
|
||||||
|
proc selectOffer(purchase: Purchase) {.async.} =
|
||||||
|
var cheapest: ?StorageOffer
|
||||||
|
for offer in purchase.offers:
|
||||||
|
if current =? cheapest:
|
||||||
|
if current.price > offer.price:
|
||||||
|
cheapest = some offer
|
||||||
|
else:
|
||||||
|
cheapest = some offer
|
||||||
|
if cheapest =? cheapest:
|
||||||
|
await purchase.market.selectOffer(cheapest.id)
|
||||||
|
|
||||||
proc run(purchase: Purchase) {.async.} =
|
proc run(purchase: Purchase) {.async.} =
|
||||||
await purchase.market.requestStorage(purchase.request)
|
proc onOffer(offer: StorageOffer) =
|
||||||
|
purchase.offers.add(offer)
|
||||||
|
let market = purchase.market
|
||||||
|
let request = purchase.request
|
||||||
|
let subscription = await market.subscribeOffers(request.id, onOffer)
|
||||||
|
await market.requestStorage(request)
|
||||||
|
await market.waitUntil(request.expiry)
|
||||||
|
await purchase.selectOffer()
|
||||||
|
await subscription.unsubscribe()
|
||||||
|
|
||||||
proc start(purchase: Purchase) =
|
proc start(purchase: Purchase) =
|
||||||
purchase.future = purchase.run()
|
purchase.future = purchase.run()
|
||||||
|
|
|
@ -1,8 +1,84 @@
|
||||||
|
import std/sequtils
|
||||||
|
import std/heapqueue
|
||||||
import pkg/dagger/market
|
import pkg/dagger/market
|
||||||
|
|
||||||
type
|
type
|
||||||
MockMarket* = ref object of Market
|
MockMarket* = ref object of Market
|
||||||
requested*: seq[StorageRequest]
|
requested*: seq[StorageRequest]
|
||||||
|
offered*: seq[StorageOffer]
|
||||||
|
selected*: seq[array[32, byte]]
|
||||||
|
subscriptions: Subscriptions
|
||||||
|
time: UInt256
|
||||||
|
waiting: HeapQueue[Expiry]
|
||||||
|
Subscriptions = object
|
||||||
|
onRequest: seq[RequestSubscription]
|
||||||
|
onOffer: seq[OfferSubscription]
|
||||||
|
RequestSubscription* = ref object of Subscription
|
||||||
|
market: MockMarket
|
||||||
|
callback: OnRequest
|
||||||
|
OfferSubscription* = ref object of Subscription
|
||||||
|
market: MockMarket
|
||||||
|
requestId: array[32, byte]
|
||||||
|
callback: OnOffer
|
||||||
|
Expiry = object
|
||||||
|
future: Future[void]
|
||||||
|
expiry: UInt256
|
||||||
|
|
||||||
method requestStorage*(market: MockMarket, request: StorageRequest) {.async.} =
|
method requestStorage*(market: MockMarket, request: StorageRequest) {.async.} =
|
||||||
market.requested.add(request)
|
market.requested.add(request)
|
||||||
|
for subscription in market.subscriptions.onRequest:
|
||||||
|
subscription.callback(request)
|
||||||
|
|
||||||
|
method offerStorage*(market: MockMarket, offer: StorageOffer) {.async.} =
|
||||||
|
market.offered.add(offer)
|
||||||
|
for subscription in market.subscriptions.onOffer:
|
||||||
|
if subscription.requestId == offer.requestId:
|
||||||
|
subscription.callback(offer)
|
||||||
|
|
||||||
|
method selectOffer*(market: MockMarket, id: array[32, byte]) {.async.} =
|
||||||
|
market.selected.add(id)
|
||||||
|
|
||||||
|
method subscribeRequests*(market: MockMarket,
|
||||||
|
callback: OnRequest):
|
||||||
|
Future[Subscription] {.async.} =
|
||||||
|
let subscription = RequestSubscription(
|
||||||
|
market: market,
|
||||||
|
callback: callback
|
||||||
|
)
|
||||||
|
market.subscriptions.onRequest.add(subscription)
|
||||||
|
return subscription
|
||||||
|
|
||||||
|
method subscribeOffers*(market: MockMarket,
|
||||||
|
requestId: array[32, byte],
|
||||||
|
callback: OnOffer):
|
||||||
|
Future[Subscription] {.async.} =
|
||||||
|
let subscription = OfferSubscription(
|
||||||
|
market: market,
|
||||||
|
requestId: requestId,
|
||||||
|
callback: callback
|
||||||
|
)
|
||||||
|
market.subscriptions.onOffer.add(subscription)
|
||||||
|
return subscription
|
||||||
|
|
||||||
|
method unsubscribe*(subscription: RequestSubscription) {.async.} =
|
||||||
|
subscription.market.subscriptions.onRequest.keepItIf(it != subscription)
|
||||||
|
|
||||||
|
method unsubscribe*(subscription: OfferSubscription) {.async.} =
|
||||||
|
subscription.market.subscriptions.onOffer.keepItIf(it != subscription)
|
||||||
|
|
||||||
|
func `<`(a, b: Expiry): bool =
|
||||||
|
a.expiry < b.expiry
|
||||||
|
|
||||||
|
method waitUntil*(market: MockMarket, expiry: UInt256): Future[void] =
|
||||||
|
let future = Future[void]()
|
||||||
|
if expiry > market.time:
|
||||||
|
market.waiting.push(Expiry(future: future, expiry: expiry))
|
||||||
|
else:
|
||||||
|
future.complete()
|
||||||
|
future
|
||||||
|
|
||||||
|
proc advanceTimeTo*(market: MockMarket, time: UInt256) =
|
||||||
|
doAssert(time >= market.time)
|
||||||
|
market.time = time
|
||||||
|
while market.waiting.len > 0 and market.waiting[0].expiry <= time:
|
||||||
|
market.waiting.pop().future.complete()
|
||||||
|
|
|
@ -21,8 +21,13 @@ suite "Purchasing":
|
||||||
contentHash: array[32, byte].example
|
contentHash: array[32, byte].example
|
||||||
)
|
)
|
||||||
|
|
||||||
|
proc purchaseAndWait(request: StorageRequest) {.async.} =
|
||||||
|
let purchase = purchasing.purchase(request)
|
||||||
|
market.advanceTimeTo(market.requested[^1].expiry)
|
||||||
|
await purchase.wait()
|
||||||
|
|
||||||
test "submits a storage request when asked":
|
test "submits a storage request when asked":
|
||||||
await purchasing.purchase(request).wait()
|
await purchaseAndWait(request)
|
||||||
let submitted = market.requested[0]
|
let submitted = market.requested[0]
|
||||||
check submitted.duration == request.duration
|
check submitted.duration == request.duration
|
||||||
check submitted.size == request.size
|
check submitted.size == request.size
|
||||||
|
@ -34,12 +39,12 @@ suite "Purchasing":
|
||||||
|
|
||||||
test "can change default value for proof probability":
|
test "can change default value for proof probability":
|
||||||
purchasing.proofProbability = 42.u256
|
purchasing.proofProbability = 42.u256
|
||||||
await purchasing.purchase(request).wait()
|
await purchaseAndWait(request)
|
||||||
check market.requested[0].proofProbability == 42.u256
|
check market.requested[0].proofProbability == 42.u256
|
||||||
|
|
||||||
test "can override proof probability per request":
|
test "can override proof probability per request":
|
||||||
request.proofProbability = 42.u256
|
request.proofProbability = 42.u256
|
||||||
await purchasing.purchase(request).wait()
|
await purchaseAndWait(request)
|
||||||
check market.requested[0].proofProbability == 42.u256
|
check market.requested[0].proofProbability == 42.u256
|
||||||
|
|
||||||
test "has a default value for request expiration interval":
|
test "has a default value for request expiration interval":
|
||||||
|
@ -48,16 +53,27 @@ suite "Purchasing":
|
||||||
test "can change default value for request expiration interval":
|
test "can change default value for request expiration interval":
|
||||||
purchasing.requestExpiryInterval = 42.u256
|
purchasing.requestExpiryInterval = 42.u256
|
||||||
let start = getTime().toUnix()
|
let start = getTime().toUnix()
|
||||||
await purchasing.purchase(request).wait()
|
await purchaseAndWait(request)
|
||||||
check market.requested[0].expiry == (start + 42).u256
|
check market.requested[0].expiry == (start + 42).u256
|
||||||
|
|
||||||
test "can override expiry time per request":
|
test "can override expiry time per request":
|
||||||
let expiry = (getTime().toUnix() + 42).u256
|
let expiry = (getTime().toUnix() + 42).u256
|
||||||
request.expiry = expiry
|
request.expiry = expiry
|
||||||
await purchasing.purchase(request).wait()
|
await purchaseAndWait(request)
|
||||||
check market.requested[0].expiry == expiry
|
check market.requested[0].expiry == expiry
|
||||||
|
|
||||||
test "includes a random nonce in every storage request":
|
test "includes a random nonce in every storage request":
|
||||||
await purchasing.purchase(request).wait()
|
await purchaseAndWait(request)
|
||||||
await purchasing.purchase(request).wait()
|
await purchaseAndWait(request)
|
||||||
check market.requested[0].nonce != market.requested[1].nonce
|
check market.requested[0].nonce != market.requested[1].nonce
|
||||||
|
|
||||||
|
test "selects the cheapest offer":
|
||||||
|
let purchase = purchasing.purchase(request)
|
||||||
|
let request = market.requested[0]
|
||||||
|
let offer1 = StorageOffer(requestId: request.id, price: 20.u256)
|
||||||
|
let offer2 = StorageOffer(requestId: request.id, price: 10.u256)
|
||||||
|
await market.offerStorage(offer1)
|
||||||
|
await market.offerStorage(offer2)
|
||||||
|
market.advanceTimeTo(request.expiry)
|
||||||
|
await purchase.wait()
|
||||||
|
check market.selected[0] == offer2.id
|
||||||
|
|
Loading…
Reference in New Issue