diff --git a/dagger/market.nim b/dagger/market.nim new file mode 100644 index 00000000..9983db79 --- /dev/null +++ b/dagger/market.nim @@ -0,0 +1,11 @@ +import pkg/chronos +import ./contracts/requests + +export chronos +export requests + +type + Market* = ref object of RootObj + +method requestStorage*(market: Market, request: StorageRequest) {.base, async.} = + raiseAssert("not implemented") diff --git a/dagger/purchasing.nim b/dagger/purchasing.nim new file mode 100644 index 00000000..655a8699 --- /dev/null +++ b/dagger/purchasing.nim @@ -0,0 +1,57 @@ +import std/times +import pkg/stint +import pkg/chronos +import pkg/questionable +import pkg/nimcrypto +import ./market + +export questionable + +type + Purchasing* = ref object + market: Market + proofProbability*: UInt256 + requestExpiryInterval*: UInt256 + PurchaseRequest* = object + duration*: UInt256 + size*: UInt256 + contentHash*: array[32, byte] + maxPrice*: UInt256 + proofProbability*: ?UInt256 + expiry*: ?UInt256 + Purchase* = ref object + +const DefaultProofProbability = 100.u256 +const DefaultRequestExpiryInterval = (10 * 60).u256 + +proc new*(_: type Purchasing, market: Market): Purchasing = + Purchasing( + market: market, + proofProbability: DefaultProofProbability, + requestExpiryInterval: DefaultRequestExpiryInterval + ) + +proc getProofProbability(purchasing: Purchasing, request: PurchaseRequest): UInt256 = + request.proofProbability |? purchasing.proofProbability + +proc getExpiry(purchasing: Purchasing, request: PurchaseRequest): UInt256 = + request.expiry |? (getTime().toUnix().u256 + purchasing.requestExpiryInterval) + +proc getNonce(): array[32, byte] = + doAssert randomBytes(result) == 32 + +proc purchase*(purchasing: Purchasing, request: PurchaseRequest): Purchase = + let request: StorageRequest = ( + client: Address.default, # TODO + duration: request.duration, + size: request.size, + contentHash: request.contentHash, + proofProbability: purchasing.getProofProbability(request), + maxPrice: request.maxPrice, + expiry: purchasing.getExpiry(request), + nonce: getNonce() + ) + asyncSpawn purchasing.market.requestStorage(request) + +proc wait*(purchase: Purchase) {.async.} = + discard diff --git a/tests/dagger/examples.nim b/tests/dagger/examples.nim index 0e72de5e..7034640f 100644 --- a/tests/dagger/examples.nim +++ b/tests/dagger/examples.nim @@ -2,9 +2,11 @@ import std/random import std/sequtils import pkg/libp2p import pkg/nitro +import pkg/stint import pkg/dagger/rng import pkg/dagger/stores import pkg/dagger/blocktype +import pkg/dagger/purchasing proc example*(_: type EthAddress): EthAddress = EthPrivateKey.random().toPublicKey.toAddress @@ -19,6 +21,13 @@ proc example*(_: type UInt48): UInt48 = # workaround for https://github.com/nim-lang/Nim/issues/17670 uint64.rand mod (UInt48.high + 1) +proc example*[T: SomeInteger](_: type T): T = + rand(T) + +proc example*[T,N](_: type array[N, T]): array[N, T] = + for item in result.mitems: + item = T.example + proc example*(_: type Wallet): Wallet = Wallet.init(EthPrivateKey.random()) @@ -53,3 +62,10 @@ proc example*(_: type BlockExcPeerCtx): BlockExcPeerCtx = proc example*(_: type Cid): Cid = Block.example.cid + +proc example*(_: type PurchaseRequest): PurchaseRequest = + PurchaseRequest( + duration: uint16.example.u256, + size: uint32.example.u256, + contentHash: array[32, byte].example + ) diff --git a/tests/dagger/helpers/mockmarket.nim b/tests/dagger/helpers/mockmarket.nim new file mode 100644 index 00000000..7f618320 --- /dev/null +++ b/tests/dagger/helpers/mockmarket.nim @@ -0,0 +1,8 @@ +import pkg/dagger/market + +type + MockMarket* = ref object of Market + requests*: seq[StorageRequest] + +method requestStorage*(market: MockMarket, request: StorageRequest) {.async.} = + market.requests.add(request) diff --git a/tests/dagger/testpurchasing.nim b/tests/dagger/testpurchasing.nim new file mode 100644 index 00000000..01ea9563 --- /dev/null +++ b/tests/dagger/testpurchasing.nim @@ -0,0 +1,59 @@ +import std/times +import pkg/asynctest +import pkg/chronos +import pkg/stint +import pkg/dagger/purchasing +import ./helpers/mockmarket +import ./examples + +suite "Purchasing": + + var purchasing: Purchasing + var market: MockMarket + var purchaseRequest: PurchaseRequest + + setup: + market = MockMarket.new() + purchasing = Purchasing.new(market) + purchaseRequest = PurchaseRequest.example + + test "submits a storage request when asked": + await purchasing.purchase(purchaseRequest).wait() + let storageRequest = market.requests[0] + check storageRequest.duration == purchaseRequest.duration + check storageRequest.size == purchaseRequest.size + check storageRequest.contentHash == purchaseRequest.contentHash + check storageRequest.maxPrice == purchaseRequest.maxPrice + + test "has a default value for proof probability": + check purchasing.proofProbability != 0.u256 + + test "can change default value for proof probability": + purchasing.proofProbability = 42.u256 + await purchasing.purchase(purchaseRequest).wait() + check market.requests[0].proofProbability == 42.u256 + + test "can override proof probability per request": + purchaseRequest.proofProbability = some 42.u256 + await purchasing.purchase(purchaseRequest).wait() + check market.requests[0].proofProbability == 42.u256 + + test "has a default value for request expiration interval": + check purchasing.requestExpiryInterval != 0.u256 + + test "can change default value for request expiration interval": + purchasing.requestExpiryInterval = 42.u256 + let start = getTime().toUnix() + await purchasing.purchase(purchaseRequest).wait() + check market.requests[0].expiry == (start + 42).u256 + + test "can override expiry time per request": + let expiry = (getTime().toUnix() + 42).u256 + purchaseRequest.expiry = some expiry + await purchasing.purchase(purchaseRequest).wait() + check market.requests[0].expiry == expiry + + test "includes a random nonce in every storage request": + await purchasing.purchase(purchaseRequest).wait() + await purchasing.purchase(purchaseRequest).wait() + check market.requests[0].nonce != market.requests[1].nonce diff --git a/tests/testDagger.nim b/tests/testDagger.nim index 37914987..9b7d2ec0 100644 --- a/tests/testDagger.nim +++ b/tests/testDagger.nim @@ -5,5 +5,6 @@ import ./dagger/testchunking import ./dagger/testmanifest import ./dagger/testnode import ./dagger/teststorestream +import ./dagger/testpurchasing {.warning[UnusedImport]: off.}