From 061b32296a2ee661bb9a23c97372e4273a252624 Mon Sep 17 00:00:00 2001 From: Mark Spanbroek Date: Wed, 30 Mar 2022 12:51:28 +0200 Subject: [PATCH] Introduce Sales --- dagger/sales.nim | 74 +++++++++++++++++++++++++++++ tests/dagger/examples.nim | 4 ++ tests/dagger/helpers/mockmarket.nim | 2 + tests/dagger/testsales.nim | 65 +++++++++++++++++++++++++ tests/testDagger.nim | 1 + 5 files changed, 146 insertions(+) create mode 100644 dagger/sales.nim create mode 100644 tests/dagger/testsales.nim diff --git a/dagger/sales.nim b/dagger/sales.nim new file mode 100644 index 00000000..9b046176 --- /dev/null +++ b/dagger/sales.nim @@ -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 diff --git a/tests/dagger/examples.nim b/tests/dagger/examples.nim index 471f16bb..a5da27a6 100644 --- a/tests/dagger/examples.nim +++ b/tests/dagger/examples.nim @@ -6,6 +6,7 @@ import pkg/stint import pkg/dagger/rng import pkg/dagger/stores import pkg/dagger/blocktype +import pkg/dagger/sales import ../examples export examples @@ -51,3 +52,6 @@ proc example*(_: type BlockExcPeerCtx): BlockExcPeerCtx = proc example*(_: type Cid): Cid = Block.example.cid + +proc example*(_: type Availability): Availability = + Availability.new(uint16.example, uint16.example, uint64.example.u256) diff --git a/tests/dagger/helpers/mockmarket.nim b/tests/dagger/helpers/mockmarket.nim index 7dd7a039..67985f72 100644 --- a/tests/dagger/helpers/mockmarket.nim +++ b/tests/dagger/helpers/mockmarket.nim @@ -2,6 +2,8 @@ import std/sequtils import std/heapqueue import pkg/dagger/market +export market + type MockMarket* = ref object of Market requested*: seq[StorageRequest] diff --git a/tests/dagger/testsales.nim b/tests/dagger/testsales.nim new file mode 100644 index 00000000..57c955f1 --- /dev/null +++ b/tests/dagger/testsales.nim @@ -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() diff --git a/tests/testDagger.nim b/tests/testDagger.nim index 9b7d2ec0..73002d2f 100644 --- a/tests/testDagger.nim +++ b/tests/testDagger.nim @@ -6,5 +6,6 @@ import ./dagger/testmanifest import ./dagger/testnode import ./dagger/teststorestream import ./dagger/testpurchasing +import ./dagger/testsales {.warning[UnusedImport]: off.}