diff --git a/dagger/contracts/market.nim b/dagger/contracts/market.nim index 7b95b014..71ea5557 100644 --- a/dagger/contracts/market.nim +++ b/dagger/contracts/market.nim @@ -59,7 +59,7 @@ method subscribeRequests(market: OnChainMarket, callback: OnRequest): Future[MarketSubscription] {.async.} = proc onEvent(event: StorageRequested) {.upraises:[].} = - callback(event.request) + callback(event.requestId, event.ask) let subscription = await market.contract.subscribe(StorageRequested, onEvent) return OnChainMarketSubscription(eventSubscription: subscription) diff --git a/dagger/contracts/requests.nim b/dagger/contracts/requests.nim index c88a3b4a..0172701d 100644 --- a/dagger/contracts/requests.nim +++ b/dagger/contracts/requests.nim @@ -8,36 +8,116 @@ export contractabi type StorageRequest* = object client*: Address - duration*: UInt256 - size*: UInt256 - contentHash*: array[32, byte] - proofProbability*: UInt256 - maxPrice*: UInt256 + ask*: StorageAsk + content*: StorageContent expiry*: UInt256 nonce*: array[32, byte] + StorageAsk* = object + size*: UInt256 + duration*: UInt256 + proofProbability*: UInt256 + maxPrice*: UInt256 + StorageContent* = object + cid*: string + erasure*: StorageErasure + por*: StoragePoR + StorageErasure* = object + totalChunks*: uint64 + totalNodes*: uint64 + nodeId*: uint64 + StoragePoR* = object + u*: seq[byte] + publicKey*: seq[byte] + name*: seq[byte] func fromTuple(_: type StorageRequest, tupl: tuple): StorageRequest = StorageRequest( client: tupl[0], - duration: tupl[1], - size: tupl[2], - contentHash: tupl[3], - proofProbability: tupl[4], - maxPrice: tupl[5], - expiry: tupl[6], - nonce: tupl[7] + ask: tupl[1], + content: tupl[2], + expiry: tupl[3], + nonce: tupl[4] ) +func fromTuple(_: type StorageAsk, tupl: tuple): StorageAsk = + StorageAsk( + size: tupl[0], + duration: tupl[1], + proofProbability: tupl[2], + maxPrice: tupl[3] + ) + +func fromTuple(_: type StorageContent, tupl: tuple): StorageContent = + StorageContent( + cid: tupl[0], + erasure: tupl[1], + por: tupl[2] + ) + +func fromTuple(_: type StorageErasure, tupl: tuple): StorageErasure = + StorageErasure( + totalChunks: tupl[0], + totalNodes: tupl[1], + nodeId: tupl[2] + ) + +func fromTuple(_: type StoragePoR, tupl: tuple): StoragePoR = + StoragePoR( + u: tupl[0], + publicKey: tupl[1], + name: tupl[2] + ) + +func solidityType*(_: type StoragePoR): string = + solidityType(StoragePoR.fieldTypes) + +func solidityType*(_: type StorageErasure): string = + solidityType(StorageErasure.fieldTypes) + +func solidityType*(_: type StorageContent): string = + solidityType(StorageContent.fieldTypes) + +func solidityType*(_: type StorageAsk): string = + solidityType(StorageAsk.fieldTypes) + func solidityType*(_: type StorageRequest): string = solidityType(StorageRequest.fieldTypes) +func encode*(encoder: var AbiEncoder, por: StoragePoR) = + encoder.write(por.fieldValues) + +func encode*(encoder: var AbiEncoder, erasure: StorageErasure) = + encoder.write(erasure.fieldValues) + +func encode*(encoder: var AbiEncoder, content: StorageContent) = + encoder.write(content.fieldValues) + +func encode*(encoder: var AbiEncoder, ask: StorageAsk) = + encoder.write(ask.fieldValues) + func encode*(encoder: var AbiEncoder, request: StorageRequest) = encoder.write(request.fieldValues) +func decode*(decoder: var AbiDecoder, T: type StoragePoR): ?!T = + let tupl = ?decoder.read(StoragePoR.fieldTypes) + success StoragePoR.fromTuple(tupl) + +func decode*(decoder: var AbiDecoder, T: type StorageErasure): ?!T = + let tupl = ?decoder.read(StorageErasure.fieldTypes) + success StorageErasure.fromTuple(tupl) + +func decode*(decoder: var AbiDecoder, T: type StorageContent): ?!T = + let tupl = ?decoder.read(StorageContent.fieldTypes) + success StorageContent.fromTuple(tupl) + +func decode*(decoder: var AbiDecoder, T: type StorageAsk): ?!T = + let tupl = ?decoder.read(StorageAsk.fieldTypes) + success StorageAsk.fromTuple(tupl) + func decode*(decoder: var AbiDecoder, T: type StorageRequest): ?!T = let tupl = ?decoder.read(StorageRequest.fieldTypes) success StorageRequest.fromTuple(tupl) func id*(request: StorageRequest): array[32, byte] = - let encoding = AbiEncoder.encode(request) + let encoding = AbiEncoder.encode((request, )) keccak256.digest(encoding).data diff --git a/dagger/contracts/storage.nim b/dagger/contracts/storage.nim index 89b47f0d..eeba433f 100644 --- a/dagger/contracts/storage.nim +++ b/dagger/contracts/storage.nim @@ -13,7 +13,7 @@ type Id = array[32, byte] StorageRequested* = object of Event requestId*: Id - request*: StorageRequest + ask*: StorageAsk StorageOffered* = object of Event offerId*: Id offer*: StorageOffer diff --git a/dagger/market.nim b/dagger/market.nim index afa6225a..c2594849 100644 --- a/dagger/market.nim +++ b/dagger/market.nim @@ -10,7 +10,7 @@ export offers type Market* = ref object of RootObj Subscription* = ref object of RootObj - OnRequest* = proc(request: StorageRequest) {.gcsafe, upraises:[].} + OnRequest* = proc(id: array[32, byte], ask: StorageAsk) {.gcsafe, upraises:[].} OnOffer* = proc(offer: StorageOffer) {.gcsafe, upraises:[].} OnSelect* = proc(offerId: array[32, byte]) {.gcsafe, upraises: [].} diff --git a/dagger/purchasing.nim b/dagger/purchasing.nim index 2ba78233..7c0a200f 100644 --- a/dagger/purchasing.nim +++ b/dagger/purchasing.nim @@ -38,8 +38,8 @@ proc new*(_: type Purchasing, market: Market): Purchasing = proc populate*(purchasing: Purchasing, request: StorageRequest): StorageRequest = result = request - if result.proofProbability == 0.u256: - result.proofProbability = purchasing.proofProbability + if result.ask.proofProbability == 0.u256: + result.ask.proofProbability = purchasing.proofProbability if result.expiry == 0.u256: result.expiry = (getTime().toUnix().u256 + purchasing.requestExpiryInterval) if result.nonce == array[32, byte].default: diff --git a/dagger/sales.nim b/dagger/sales.nim index abd9b719..4b21e055 100644 --- a/dagger/sales.nim +++ b/dagger/sales.nim @@ -24,7 +24,8 @@ type minPrice*: UInt256 Negotiation = ref object sales: Sales - request: StorageRequest + requestId: array[32, byte] + ask: StorageAsk availability: Availability offer: ?StorageOffer subscription: ?Subscription @@ -52,17 +53,17 @@ func add*(sales: Sales, availability: Availability) = func remove*(sales: Sales, availability: Availability) = sales.available.keepItIf(it != availability) -func findAvailability(sales: Sales, request: StorageRequest): ?Availability = +func findAvailability(sales: Sales, ask: StorageAsk): ?Availability = for availability in sales.available: - if request.size <= availability.size.u256 and - request.duration <= availability.duration.u256 and - request.maxPrice >= availability.minPrice: + if ask.size <= availability.size.u256 and + ask.duration <= availability.duration.u256 and + ask.maxPrice >= availability.minPrice: return some availability proc createOffer(negotiation: Negotiation): StorageOffer = StorageOffer( - requestId: negotiation.request.id, - price: negotiation.request.maxPrice, + requestId: negotiation.requestId, + price: negotiation.ask.maxPrice, expiry: getTime().toUnix().u256 + negotiation.sales.offerExpiryInterval ) @@ -117,13 +118,16 @@ proc start(negotiation: Negotiation) {.async.} = await negotiation.subscribeSelect() negotiation.waiting = some negotiation.waitForExpiry() -proc handleRequest(sales: Sales, request: StorageRequest) {.async.} = - without availability =? sales.findAvailability(request): +proc handleRequest(sales: Sales, + requestId: array[32, byte], + ask: StorageAsk) {.async.} = + without availability =? sales.findAvailability(ask): return let negotiation = Negotiation( sales: sales, - request: request, + requestId: requestId, + ask: ask, availability: availability ) @@ -132,8 +136,8 @@ proc handleRequest(sales: Sales, request: StorageRequest) {.async.} = proc start*(sales: Sales) = doAssert sales.subscription.isNone, "Sales already started" - proc onRequest(request: StorageRequest) {.gcsafe, upraises:[].} = - asyncSpawn sales.handleRequest(request) + proc onRequest(requestId: array[32, byte], ask: StorageAsk) {.gcsafe, upraises:[].} = + asyncSpawn sales.handleRequest(requestId, ask) proc subscribe {.async.} = sales.subscription = some await sales.market.subscribeRequests(onRequest) diff --git a/tests/contracts/examples.nim b/tests/contracts/examples.nim index e817bf59..3dbbd888 100644 --- a/tests/contracts/examples.nim +++ b/tests/contracts/examples.nim @@ -1,6 +1,5 @@ import std/times import pkg/stint -import pkg/nimcrypto import pkg/ethers import dagger/contracts import ../examples @@ -13,11 +12,25 @@ proc example*(_: type Address): Address = proc example*(_: type StorageRequest): StorageRequest = StorageRequest( client: Address.example, - duration: (10 * 60 * 60).u256, # 10 hours - size: (1 * 1024 * 1024 * 1024).u256, # 1 Gigabyte - contentHash: sha256.digest(0xdeadbeef'u32.toBytes).data, - proofProbability: 4.u256, # require a proof roughly once every 4 periods - maxPrice: 84.u256, + ask: StorageAsk( + size: (1 * 1024 * 1024 * 1024).u256, # 1 Gigabyte + duration: (10 * 60 * 60).u256, # 10 hours + proofProbability: 4.u256, # require a proof roughly once every 4 periods + maxPrice: 84.u256 + ), + content: StorageContent( + cid: "zb2rhheVmk3bLks5MgzTqyznLu1zqGH5jrfTA1eAZXrjx7Vob", + erasure: StorageErasure( + totalChunks: 12, + totalNodes: 4, + nodeId: 3 + ), + por: StoragePor( + u: @(array[480, byte].example), + publicKey: @(array[96, byte].example), + name: @(array[512, byte].example) + ) + ), expiry: (getTime() + initDuration(hours=1)).toUnix.u256, nonce: array[32, byte].example ) diff --git a/tests/contracts/testContracts.nim b/tests/contracts/testContracts.nim index 2513586d..5104e8ee 100644 --- a/tests/contracts/testContracts.nim +++ b/tests/contracts/testContracts.nim @@ -45,7 +45,7 @@ ethersuite "Storage contracts": offer.requestId = request.id switchAccount(client) - await token.approve(storage.address, request.maxPrice) + await token.approve(storage.address, request.ask.maxPrice) await storage.requestStorage(request) switchAccount(host) await token.approve(storage.address, collateralAmount) diff --git a/tests/contracts/testMarket.nim b/tests/contracts/testMarket.nim index 13fe763b..7db5264d 100644 --- a/tests/contracts/testMarket.nim +++ b/tests/contracts/testMarket.nim @@ -30,7 +30,7 @@ ethersuite "On-Chain Market": request.client = accounts[0] offer.host = accounts[0] offer.requestId = request.id - offer.price = request.maxPrice + offer.price = request.ask.maxPrice test "fails to instantiate when contract does not have a signer": let storageWithoutSigner = storage.connect(provider) @@ -38,33 +38,36 @@ ethersuite "On-Chain Market": discard OnChainMarket.new(storageWithoutSigner) test "supports storage requests": - await token.approve(storage.address, request.maxPrice) + await token.approve(storage.address, request.ask.maxPrice) check (await market.requestStorage(request)) == request test "sets client address when submitting storage request": var requestWithoutClient = request requestWithoutClient.client = Address.default - await token.approve(storage.address, request.maxPrice) + await token.approve(storage.address, request.ask.maxPrice) let submitted = await market.requestStorage(requestWithoutClient) check submitted.client == accounts[0] test "supports request subscriptions": - var received: seq[StorageRequest] - proc onRequest(request: StorageRequest) = - received.add(request) + var receivedIds: seq[array[32, byte]] + var receivedAsks: seq[StorageAsk] + proc onRequest(id: array[32, byte], ask: StorageAsk) = + receivedIds.add(id) + receivedAsks.add(ask) let subscription = await market.subscribeRequests(onRequest) - await token.approve(storage.address, request.maxPrice) + await token.approve(storage.address, request.ask.maxPrice) discard await market.requestStorage(request) - check received == @[request] + check receivedIds == @[request.id] + check receivedAsks == @[request.ask] await subscription.unsubscribe() test "supports storage offers": - await token.approve(storage.address, request.maxPrice) + await token.approve(storage.address, request.ask.maxPrice) discard await market.requestStorage(request) check (await market.offerStorage(offer)) == offer test "sets host address when submitting storage offer": - await token.approve(storage.address, request.maxPrice) + await token.approve(storage.address, request.ask.maxPrice) discard await market.requestStorage(request) var offerWithoutHost = offer offerWithoutHost.host = Address.default @@ -72,7 +75,7 @@ ethersuite "On-Chain Market": check submitted.host == accounts[0] test "supports offer subscriptions": - await token.approve(storage.address, request.maxPrice) + await token.approve(storage.address, request.ask.maxPrice) discard await market.requestStorage(request) var received: seq[StorageOffer] proc onOffer(offer: StorageOffer) = @@ -88,11 +91,11 @@ ethersuite "On-Chain Market": otherRequest.client = accounts[0] otherOffer.host = accounts[0] otherOffer.requestId = otherRequest.id - otherOffer.price = otherRequest.maxPrice + otherOffer.price = otherrequest.ask.maxPrice - await token.approve(storage.address, request.maxPrice) + await token.approve(storage.address, request.ask.maxPrice) discard await market.requestStorage(request) - await token.approve(storage.address, otherRequest.maxPrice) + await token.approve(storage.address, otherrequest.ask.maxPrice) discard await market.requestStorage(otherRequest) var submitted: seq[StorageOffer] @@ -109,7 +112,7 @@ ethersuite "On-Chain Market": await subscription.unsubscribe() test "supports selection of an offer": - await token.approve(storage.address, request.maxPrice) + await token.approve(storage.address, request.ask.maxPrice) discard await market.requestStorage(request) discard await market.offerStorage(offer) @@ -130,12 +133,12 @@ ethersuite "On-Chain Market": otherRequest.client = accounts[0] otherOffer.host = accounts[0] otherOffer.requestId = otherRequest.id - otherOffer.price = otherRequest.maxPrice + otherOffer.price = otherrequest.ask.maxPrice - await token.approve(storage.address, request.maxPrice) + await token.approve(storage.address, request.ask.maxPrice) discard await market.requestStorage(request) discard await market.offerStorage(offer) - await token.approve(storage.address, otherRequest.maxPrice) + await token.approve(storage.address, otherrequest.ask.maxPrice) discard await market.requestStorage(otherRequest) discard await market.offerStorage(otherOffer) diff --git a/tests/dagger/helpers/mockmarket.nim b/tests/dagger/helpers/mockmarket.nim index de1f551b..d846e706 100644 --- a/tests/dagger/helpers/mockmarket.nim +++ b/tests/dagger/helpers/mockmarket.nim @@ -38,7 +38,7 @@ method requestStorage*(market: MockMarket, market.requested.add(request) let subscriptions = market.subscriptions.onRequest for subscription in subscriptions: - subscription.callback(request) + subscription.callback(request.id, request.ask) return request method offerStorage*(market: MockMarket, diff --git a/tests/dagger/testpurchasing.nim b/tests/dagger/testpurchasing.nim index 775825bd..0c87440a 100644 --- a/tests/dagger/testpurchasing.nim +++ b/tests/dagger/testpurchasing.nim @@ -16,9 +16,10 @@ suite "Purchasing": market = MockMarket.new() purchasing = Purchasing.new(market) request = StorageRequest( - duration: uint16.example.u256, - size: uint32.example.u256, - contentHash: array[32, byte].example + ask: StorageAsk( + duration: uint16.example.u256, + size: uint32.example.u256, + ) ) proc purchaseAndWait(request: StorageRequest) {.async.} = @@ -29,10 +30,9 @@ suite "Purchasing": test "submits a storage request when asked": await purchaseAndWait(request) let submitted = market.requested[0] - check submitted.duration == request.duration - check submitted.size == request.size - check submitted.contentHash == request.contentHash - check submitted.maxPrice == request.maxPrice + check submitted.ask.duration == request.ask.duration + check submitted.ask.size == request.ask.size + check submitted.ask.maxPrice == request.ask.maxPrice test "has a default value for proof probability": check purchasing.proofProbability != 0.u256 @@ -40,12 +40,12 @@ suite "Purchasing": test "can change default value for proof probability": purchasing.proofProbability = 42.u256 await purchaseAndWait(request) - check market.requested[0].proofProbability == 42.u256 + check market.requested[0].ask.proofProbability == 42.u256 test "can override proof probability per request": - request.proofProbability = 42.u256 + request.ask.proofProbability = 42.u256 await purchaseAndWait(request) - check market.requested[0].proofProbability == 42.u256 + check market.requested[0].ask.proofProbability == 42.u256 test "has a default value for request expiration interval": check purchasing.requestExpiryInterval != 0.u256 diff --git a/tests/dagger/testsales.nim b/tests/dagger/testsales.nim index 653f75df..6e1152d3 100644 --- a/tests/dagger/testsales.nim +++ b/tests/dagger/testsales.nim @@ -8,7 +8,9 @@ import ./examples suite "Sales": let availability = Availability.init(size=100, duration=60, minPrice=42.u256) - let request = StorageRequest(duration: 60.u256, size: 100.u256, maxPrice:42.u256) + let request = StorageRequest( + ask: StorageAsk(duration: 60.u256, size: 100.u256, maxPrice:42.u256) + ) var sales: Sales var market: MockMarket @@ -52,7 +54,7 @@ suite "Sales": test "ignores request when no matching storage is available": sales.add(availability) var tooBig = request - tooBig.size = request.size + 1 + tooBig.ask.size = request.ask.size + 1 discard await market.requestStorage(tooBig) check market.offered.len == 0 diff --git a/vendor/dagger-contracts b/vendor/dagger-contracts index 29b57759..6aa28945 160000 --- a/vendor/dagger-contracts +++ b/vendor/dagger-contracts @@ -1 +1 @@ -Subproject commit 29b5775951e774cb170b23cb6772cd7f1e7b5499 +Subproject commit 6aa2894521faa53a9896e800caf713499b33c318