Introduce Sales

This commit is contained in:
Mark Spanbroek 2022-03-30 12:51:28 +02:00 committed by markspanbroek
parent 75ec8c0bfd
commit 061b32296a
5 changed files with 146 additions and 0 deletions

74
dagger/sales.nim Normal file
View File

@ -0,0 +1,74 @@
import std/sequtils
import pkg/questionable
import pkg/upraises
import pkg/stint
import pkg/nimcrypto
import ./market
export stint
type
Sales* = ref object
market: Market
available*: seq[Availability]
subscription: ?Subscription
Availability* = object
id*: array[32, byte]
size*: uint64
duration*: uint64
minPrice*: UInt256
func new*(_: type Sales, market: Market): Sales =
Sales(market: market)
proc new*(_: type Availability,
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)
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
func createOffer(sales: Sales,
request: StorageRequest,
availability: Availability): StorageOffer =
StorageOffer(
requestId: request.id,
price: request.maxPrice
)
proc handleRequest(sales: Sales, request: StorageRequest) {.async.} =
if availability =? sales.findAvailability(request):
sales.remove(availability)
let offer = sales.createOffer(request, availability)
await sales.market.offerStorage(offer)
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

View File

@ -6,6 +6,7 @@ import pkg/stint
import pkg/dagger/rng import pkg/dagger/rng
import pkg/dagger/stores import pkg/dagger/stores
import pkg/dagger/blocktype import pkg/dagger/blocktype
import pkg/dagger/sales
import ../examples import ../examples
export examples export examples
@ -51,3 +52,6 @@ proc example*(_: type BlockExcPeerCtx): BlockExcPeerCtx =
proc example*(_: type Cid): Cid = proc example*(_: type Cid): Cid =
Block.example.cid Block.example.cid
proc example*(_: type Availability): Availability =
Availability.new(uint16.example, uint16.example, uint64.example.u256)

View File

@ -2,6 +2,8 @@ import std/sequtils
import std/heapqueue import std/heapqueue
import pkg/dagger/market import pkg/dagger/market
export market
type type
MockMarket* = ref object of Market MockMarket* = ref object of Market
requested*: seq[StorageRequest] requested*: seq[StorageRequest]

View File

@ -0,0 +1,65 @@
import pkg/asynctest
import pkg/chronos
import pkg/dagger/sales
import ./helpers/mockmarket
import ./examples
suite "Sales":
var sales: Sales
var market: MockMarket
setup:
market = MockMarket.new()
sales = Sales.new(market)
test "has no availability initially":
check sales.available.len == 0
test "can add available storage":
let availability1 = Availability.example
let availability2 = Availability.example
sales.add(availability1)
check sales.available.contains(availability1)
sales.add(availability2)
check sales.available.contains(availability1)
check sales.available.contains(availability2)
test "can remove available storage":
let availability = Availability.example
sales.add(availability)
sales.remove(availability)
check sales.available.len == 0
test "generates unique ids for storage availability":
let availability1 = Availability.new(size=1, duration=2, minPrice=3.u256)
let availability2 = Availability.new(size=1, duration=2, minPrice=3.u256)
check availability1.id != availability2.id
test "offers available storage when matching request comes in":
let availability = Availability.new(size=100, duration=60, minPrice=42.u256)
sales.add(availability)
sales.start()
let request = StorageRequest(duration:60.u256, size:100.u256, maxPrice:42.u256)
await market.requestStorage(request)
check market.offered.len == 1
check market.offered[0].price == 42.u256
sales.stop()
test "ignores request when no matching storage is available":
let availability = Availability.new(size=99, duration=60, minPrice=42.u256)
sales.add(availability)
sales.start()
let request = StorageRequest(duration:60.u256, size:100.u256, maxPrice:42.u256)
await market.requestStorage(request)
check market.offered.len == 0
sales.stop()
test "makes storage unavailable when offer is submitted":
let availability = Availability.new(size=100, duration=60, minPrice=42.u256)
sales.add(availability)
sales.start()
let request = StorageRequest(duration:60.u256, size:100.u256, maxPrice:42.u256)
await market.requestStorage(request)
check sales.available.len == 0
sales.stop()

View File

@ -6,5 +6,6 @@ import ./dagger/testmanifest
import ./dagger/testnode import ./dagger/testnode
import ./dagger/teststorestream import ./dagger/teststorestream
import ./dagger/testpurchasing import ./dagger/testpurchasing
import ./dagger/testsales
{.warning[UnusedImport]: off.} {.warning[UnusedImport]: off.}