diff --git a/codex/codex.nim b/codex/codex.nim index 391a94fc..96224c22 100644 --- a/codex/codex.nim +++ b/codex/codex.nim @@ -109,9 +109,12 @@ proc bootstrapInteractions(s: CodexServer): Future[void] {.async.} = quit QuitFailure let marketplace = Marketplace.new(marketplaceAddress, signer) - let market = OnChainMarket.new( + without market =? await OnChainMarket.load( marketplace, config.rewardRecipient, config.marketplaceRequestCacheSize - ) + ), error: + fatal "Cannot load market", error = error.msg + quit QuitFailure + let clock = OnChainClock.new(provider) var client: ?ClientInteractions @@ -134,10 +137,6 @@ proc bootstrapInteractions(s: CodexServer): Future[void] {.async.} = if config.simulateProofFailures > 0: warn "Proof failure simulation is not enabled for this build! Configuration ignored" - if error =? (await market.loadConfig()).errorOption: - fatal "Cannot load market configuration", error = error.msg - quit QuitFailure - let purchasing = Purchasing.new(market, clock) let sales = Sales.new(market, clock, repo, proofFailures) client = some ClientInteractions.new(clock, purchasing) diff --git a/codex/contracts/market.nim b/codex/contracts/market.nim index 25e57fac..53768a79 100644 --- a/codex/contracts/market.nim +++ b/codex/contracts/market.nim @@ -21,7 +21,7 @@ type contract: Marketplace signer: Signer rewardRecipient: ?Address - configuration: ?MarketplaceConfig + configuration: MarketplaceConfig requestCache: LruCache[string, StorageRequest] MarketSubscription = market.Subscription @@ -29,24 +29,40 @@ type OnChainMarketSubscription = ref object of MarketSubscription eventSubscription: EventSubscription -func new*( +proc loadConfig( + market: OnChainMarket +): Future[?!MarketplaceConfig] {.async: (raises: [CancelledError]).} = + try: + return success await market.contract.configuration() + except EthersError: + let err = getCurrentException() + return failure newException( + MarketError, + "Failed to fetch the config from the Marketplace contract: " & err.msg, + ) + +proc load*( _: type OnChainMarket, contract: Marketplace, rewardRecipient = Address.none, requestCacheSize: uint16 = DefaultRequestCacheSize, -): OnChainMarket = +): Future[?!OnChainMarket] {.async: (raises: [CancelledError]).} = without signer =? contract.signer: raiseAssert("Marketplace contract should have a signer") var requestCache = newLruCache[string, StorageRequest](int(requestCacheSize)) - OnChainMarket( + let market = OnChainMarket( contract: contract, signer: signer, rewardRecipient: rewardRecipient, requestCache: requestCache, ) + market.configuration = ? await market.loadConfig() + + return success market + proc raiseMarketError(message: string) {.raises: [MarketError].} = raise newException(MarketError, message) @@ -62,20 +78,6 @@ template convertEthersError(msg: string = "", body) = except EthersError as error: raiseMarketError(error.msgDetail.prefixWith(msg)) -proc config( - market: OnChainMarket -): Future[MarketplaceConfig] {.async: (raises: [CancelledError, MarketError]).} = - without resolvedConfig =? market.configuration: - if err =? (await market.loadConfig()).errorOption: - raiseMarketError(err.msg) - - without config =? market.configuration: - raiseMarketError("Failed to access to config from the Marketplace contract") - - return config - - return resolvedConfig - proc approveFunds( market: OnChainMarket, amount: UInt256 ) {.async: (raises: [CancelledError, MarketError]).} = @@ -85,67 +87,30 @@ proc approveFunds( let token = Erc20Token.new(tokenAddress, market.signer) discard await token.increaseAllowance(market.contract.address(), amount).confirm(1) -method loadConfig*( - market: OnChainMarket -): Future[?!void] {.async: (raises: [CancelledError]).} = - try: - without config =? market.configuration: - let fetchedConfig = await market.contract.configuration() - - market.configuration = some fetchedConfig - - return success() - except EthersError as err: - return failure newException( - MarketError, - "Failed to fetch the config from the Marketplace contract: " & err.msg, - ) - -method getZkeyHash*( - market: OnChainMarket -): Future[?string] {.async: (raises: [CancelledError, MarketError]).} = - let config = await market.config() - return some config.proofs.zkeyHash - method getSigner*( market: OnChainMarket ): Future[Address] {.async: (raises: [CancelledError, MarketError]).} = convertEthersError("Failed to get signer address"): return await market.signer.getAddress() -method periodicity*( - market: OnChainMarket -): Future[Periodicity] {.async: (raises: [CancelledError, MarketError]).} = - convertEthersError("Failed to get Marketplace config"): - let config = await market.config() - let period = config.proofs.period - return Periodicity(seconds: period) +method zkeyHash*(market: OnChainMarket): string = + return market.configuration.proofs.zkeyHash -method proofTimeout*( - market: OnChainMarket -): Future[uint64] {.async: (raises: [CancelledError, MarketError]).} = - convertEthersError("Failed to get Marketplace config"): - let config = await market.config() - return config.proofs.timeout +method periodicity*(market: OnChainMarket): Periodicity = + let period = market.configuration.proofs.period + return Periodicity(seconds: period) -method repairRewardPercentage*( - market: OnChainMarket -): Future[uint8] {.async: (raises: [CancelledError, MarketError]).} = - convertEthersError("Failed to get Marketplace config"): - let config = await market.config() - return config.collateral.repairRewardPercentage +method proofTimeout*(market: OnChainMarket): uint64 = + return market.configuration.proofs.timeout -method requestDurationLimit*(market: OnChainMarket): Future[uint64] {.async.} = - convertEthersError("Failed to get Marketplace config"): - let config = await market.config() - return config.requestDurationLimit +method repairRewardPercentage*(market: OnChainMarket): uint8 = + return market.configuration.collateral.repairRewardPercentage -method proofDowntime*( - market: OnChainMarket -): Future[uint8] {.async: (raises: [CancelledError, MarketError]).} = - convertEthersError("Failed to get Marketplace config"): - let config = await market.config() - return config.proofs.downtime +method requestDurationLimit*(market: OnChainMarket): uint64 = + return market.configuration.requestDurationLimit + +method proofDowntime*(market: OnChainMarket): uint8 = + return market.configuration.proofs.downtime method getPointer*(market: OnChainMarket, slotId: SlotId): Future[uint8] {.async.} = convertEthersError("Failed to get slot pointer"): diff --git a/codex/market.nim b/codex/market.nim index 4350b2eb..5f73c3ae 100644 --- a/codex/market.nim +++ b/codex/market.nim @@ -64,51 +64,32 @@ type ProofSubmitted* = object of MarketplaceEvent id*: SlotId -method loadConfig*( - market: Market -): Future[?!void] {.base, async: (raises: [CancelledError]).} = - raiseAssert("not implemented") - -method getZkeyHash*( - market: Market -): Future[?string] {.base, async: (raises: [CancelledError, MarketError]).} = - raiseAssert("not implemented") - method getSigner*( market: Market ): Future[Address] {.base, async: (raises: [CancelledError, MarketError]).} = raiseAssert("not implemented") -method periodicity*( - market: Market -): Future[Periodicity] {.base, async: (raises: [CancelledError, MarketError]).} = +method periodicity*(market: Market): Periodicity {.base, gcsafe, raises:[].} = raiseAssert("not implemented") -method proofTimeout*( - market: Market -): Future[uint64] {.base, async: (raises: [CancelledError, MarketError]).} = +method proofTimeout*(market: Market): uint64 {.base, gcsafe, raises:[].} = raiseAssert("not implemented") -method repairRewardPercentage*( - market: Market -): Future[uint8] {.base, async: (raises: [CancelledError, MarketError]).} = +method repairRewardPercentage*(market: Market): uint8 {.base, gcsafe, raises:[].} = raiseAssert("not implemented") -method requestDurationLimit*(market: Market): Future[uint64] {.base, async.} = +method requestDurationLimit*(market: Market): uint64 {.base, gcsafe, raises:[].} = raiseAssert("not implemented") -method proofDowntime*( - market: Market -): Future[uint8] {.base, async: (raises: [CancelledError, MarketError]).} = +method proofDowntime*(market: Market): uint8 {.base, gcsafe, raises: [].} = raiseAssert("not implemented") method getPointer*(market: Market, slotId: SlotId): Future[uint8] {.base, async.} = raiseAssert("not implemented") proc inDowntime*(market: Market, slotId: SlotId): Future[bool] {.async.} = - let downtime = await market.proofDowntime let pntr = await market.getPointer(slotId) - return pntr < downtime + return pntr < market.proofDowntime method requestStorage*( market: Market, request: StorageRequest diff --git a/codex/rest/api.nim b/codex/rest/api.nim index ee493e03..39b9cabf 100644 --- a/codex/rest/api.nim +++ b/codex/rest/api.nim @@ -705,7 +705,8 @@ proc initPurchasingApi(node: CodexNodeRef, router: var RestRouter) = headers = headers, ) - let requestDurationLimit = await contracts.purchasing.market.requestDurationLimit + let requestDurationLimit = contracts.purchasing.market.requestDurationLimit + if params.duration > requestDurationLimit: return RestApiResponse.error( Http422, diff --git a/codex/sales/states/initialproving.nim b/codex/sales/states/initialproving.nim index 57e8cc2c..1967b9fd 100644 --- a/codex/sales/states/initialproving.nim +++ b/codex/sales/states/initialproving.nim @@ -30,8 +30,8 @@ proc waitUntilNextPeriod(clock: Clock, periodicity: Periodicity) {.async.} = await clock.waitUntil((periodEnd + 1).toSecondsSince1970) proc waitForStableChallenge(market: Market, clock: Clock, slotId: SlotId) {.async.} = - let periodicity = await market.periodicity() - let downtime = await market.proofDowntime() + let periodicity = market.periodicity + let downtime = market.proofDowntime await clock.waitUntilNextPeriod(periodicity) while (await market.getPointer(slotId)) > (256 - downtime): await clock.waitUntilNextPeriod(periodicity) diff --git a/codex/sales/states/proving.nim b/codex/sales/states/proving.nim index 690e9136..d2818e5a 100644 --- a/codex/sales/states/proving.nim +++ b/codex/sales/states/proving.nim @@ -58,17 +58,17 @@ proc proveLoop( slotIndex slotId = slot.id - proc getCurrentPeriod(): Future[Period] {.async.} = - let periodicity = await market.periodicity() + proc getCurrentPeriod(): Period = + let periodicity = market.periodicity return periodicity.periodOf(clock.now().Timestamp) proc waitUntilPeriod(period: Period) {.async.} = - let periodicity = await market.periodicity() + let periodicity = market.periodicity # Ensure that we're past the period boundary by waiting an additional second await clock.waitUntil((periodicity.periodStart(period) + 1).toSecondsSince1970) while true: - let currentPeriod = await getCurrentPeriod() + let currentPeriod = getCurrentPeriod() let slotState = await market.slotState(slot.id) case slotState diff --git a/codex/validation.nim b/codex/validation.nim index e6d74840..b42913ba 100644 --- a/codex/validation.nim +++ b/codex/validation.nim @@ -120,7 +120,7 @@ proc findEpoch(validation: Validation, secondsAgo: uint64): SecondsSince1970 = proc restoreHistoricalState(validation: Validation) {.async.} = trace "Restoring historical state..." - let requestDurationLimit = await validation.market.requestDurationLimit + let requestDurationLimit = validation.market.requestDurationLimit let startTimeEpoch = validation.findEpoch(secondsAgo = requestDurationLimit) let slotFilledEvents = await validation.market.queryPastSlotFilledEvents(fromTime = startTimeEpoch) @@ -137,8 +137,8 @@ proc restoreHistoricalState(validation: Validation) {.async.} = proc start*(validation: Validation) {.async.} = trace "Starting validator", groups = validation.config.groups, groupIndex = validation.config.groupIndex - validation.periodicity = await validation.market.periodicity() - validation.proofTimeout = await validation.market.proofTimeout() + validation.periodicity = validation.market.periodicity + validation.proofTimeout = validation.market.proofTimeout await validation.subscribeSlotFilled() await validation.restoreHistoricalState() validation.running = validation.run() diff --git a/tests/codex/helpers/mockmarket.nim b/tests/codex/helpers/mockmarket.nim index 52ca1a78..a1290e5f 100644 --- a/tests/codex/helpers/mockmarket.nim +++ b/tests/codex/helpers/mockmarket.nim @@ -142,37 +142,24 @@ proc new*(_: type MockMarket, clock: ?Clock = Clock.none): MockMarket = signer: Address.example, config: config, canReserveSlot: true, clock: clock ) -method loadConfig*( - market: MockMarket -): Future[?!void] {.async: (raises: [CancelledError]).} = - discard - method getSigner*( market: MockMarket ): Future[Address] {.async: (raises: [CancelledError, MarketError]).} = return market.signer -method periodicity*( - mock: MockMarket -): Future[Periodicity] {.async: (raises: [CancelledError, MarketError]).} = +method periodicity*(mock: MockMarket): Periodicity = return Periodicity(seconds: mock.config.proofs.period) -method proofTimeout*( - market: MockMarket -): Future[uint64] {.async: (raises: [CancelledError, MarketError]).} = +method proofTimeout*(market: MockMarket): uint64 = return market.config.proofs.timeout -method requestDurationLimit*(market: MockMarket): Future[uint64] {.async.} = +method requestDurationLimit*(market: MockMarket): uint64 = return market.config.requestDurationLimit -method proofDowntime*( - market: MockMarket -): Future[uint8] {.async: (raises: [CancelledError, MarketError]).} = +method proofDowntime*(market: MockMarket): uint8 = return market.config.proofs.downtime -method repairRewardPercentage*( - market: MockMarket -): Future[uint8] {.async: (raises: [CancelledError, MarketError]).} = +method repairRewardPercentage*(market: MockMarket): uint8 = return market.config.collateral.repairRewardPercentage method getPointer*(market: MockMarket, slotId: SlotId): Future[uint8] {.async.} = diff --git a/tests/codex/sales/helpers/periods.nim b/tests/codex/sales/helpers/periods.nim index 99716cec..e5f421df 100644 --- a/tests/codex/sales/helpers/periods.nim +++ b/tests/codex/sales/helpers/periods.nim @@ -1,8 +1,8 @@ import pkg/codex/market import ../../helpers/mockclock -proc advanceToNextPeriod*(clock: MockClock, market: Market) {.async.} = - let periodicity = await market.periodicity() +proc advanceToNextPeriod*(clock: MockClock, market: Market) = + let periodicity = market.periodicity() let period = periodicity.periodOf(clock.now().Timestamp) let periodEnd = periodicity.periodEnd(period) clock.set(periodEnd.toSecondsSince1970 + 1) diff --git a/tests/codex/sales/states/testinitialproving.nim b/tests/codex/sales/states/testinitialproving.nim index cae0a069..3d082f8a 100644 --- a/tests/codex/sales/states/testinitialproving.nim +++ b/tests/codex/sales/states/testinitialproving.nim @@ -40,7 +40,7 @@ asyncchecksuite "sales state 'initialproving'": proc allowProofToStart() {.async.} = # it won't start proving until the next period - await clock.advanceToNextPeriod(market) + clock.advanceToNextPeriod(market) test "switches to cancelled state when request expires": let next = state.onCancelled(request) diff --git a/tests/codex/sales/states/testproving.nim b/tests/codex/sales/states/testproving.nim index 6b7e7bd4..9ce8d332 100644 --- a/tests/codex/sales/states/testproving.nim +++ b/tests/codex/sales/states/testproving.nim @@ -14,6 +14,7 @@ import ../../examples import ../../helpers import ../../helpers/mockmarket import ../../helpers/mockclock +import ../helpers/periods asyncchecksuite "sales state 'proving'": let slot = Slot.example @@ -38,12 +39,6 @@ asyncchecksuite "sales state 'proving'": agent = newSalesAgent(context, request.id, slot.slotIndex, request.some) state = SaleProving.new() - proc advanceToNextPeriod(market: Market) {.async.} = - let periodicity = await market.periodicity() - let current = periodicity.periodOf(clock.now().Timestamp) - let periodEnd = periodicity.periodEnd(current) - clock.set(periodEnd.toSecondsSince1970 + 1) - test "switches to cancelled state when request expires": let next = state.onCancelled(request) check !next of SaleCancelled @@ -64,7 +59,7 @@ asyncchecksuite "sales state 'proving'": let future = state.run(agent) market.setProofRequired(slot.id, true) - await market.advanceToNextPeriod() + clock.advanceToNextPeriod(market) check eventually receivedIds.contains(slot.id) @@ -77,7 +72,7 @@ asyncchecksuite "sales state 'proving'": let future = state.run(agent) market.slotState[slot.id] = SlotState.Finished - await market.advanceToNextPeriod() + clock.advanceToNextPeriod(market) check eventually future.finished check !(future.read()) of SalePayout @@ -88,7 +83,7 @@ asyncchecksuite "sales state 'proving'": let future = state.run(agent) market.slotState[slot.id] = SlotState.Free - await market.advanceToNextPeriod() + clock.advanceToNextPeriod(market) check eventually future.finished check !(future.read()) of SaleErrored diff --git a/tests/codex/sales/states/testsimulatedproving.nim b/tests/codex/sales/states/testsimulatedproving.nim index c8f4ae1d..f778c457 100644 --- a/tests/codex/sales/states/testsimulatedproving.nim +++ b/tests/codex/sales/states/testsimulatedproving.nim @@ -14,6 +14,7 @@ import ../../examples import ../../helpers import ../../helpers/mockmarket import ../../helpers/mockclock +import ../helpers/periods asyncchecksuite "sales state 'simulated-proving'": let slot = Slot.example @@ -54,16 +55,10 @@ asyncchecksuite "sales state 'simulated-proving'": teardown: await subscription.unsubscribe() - proc advanceToNextPeriod(market: Market) {.async.} = - let periodicity = await market.periodicity() - let current = periodicity.periodOf(clock.now().Timestamp) - let periodEnd = periodicity.periodEnd(current) - clock.set(periodEnd.toSecondsSince1970 + 1) - proc waitForProvingRounds(market: Market, rounds: int) {.async.} = var rnds = rounds - 1 # proof round runs prior to advancing while rnds > 0: - await market.advanceToNextPeriod() + clock.advanceToNextPeriod(market) await proofSubmitted rnds -= 1 @@ -90,7 +85,7 @@ asyncchecksuite "sales state 'simulated-proving'": let future = state.run(agent) market.slotState[slot.id] = SlotState.Finished - await market.advanceToNextPeriod() + clock.advanceToNextPeriod(market) check eventually future.finished check !(future.read()) of SalePayout diff --git a/tests/codex/sales/testsales.nim b/tests/codex/sales/testsales.nim index 6fb42933..af8927ca 100644 --- a/tests/codex/sales/testsales.nim +++ b/tests/codex/sales/testsales.nim @@ -218,7 +218,7 @@ asyncchecksuite "Sales": proc allowRequestToStart() {.async.} = check eventually isInState(0, "SaleInitialProving") # it won't start proving until the next period - await clock.advanceToNextPeriod(market) + clock.advanceToNextPeriod(market) proc getAvailability(): Availability = let key = availability.id.key.get diff --git a/tests/contracts/testMarket.nim b/tests/contracts/testMarket.nim index 97b9bbdd..c270bd10 100644 --- a/tests/contracts/testMarket.nim +++ b/tests/contracts/testMarket.nim @@ -36,10 +36,10 @@ ethersuite "On-Chain Market": ): UInt256 = return (endTimestamp - startTimestamp) * r.ask.pricePerSlotPerSecond - proc switchAccount(account: Signer) = + proc switchAccount(account: Signer) {.async.} = marketplace = marketplace.connect(account) token = token.connect(account) - market = OnChainMarket.new(marketplace, market.rewardRecipient) + market = ! await OnChainMarket.load(marketplace, market.rewardRecipient) setup: let address = Marketplace.address(dummyVerifier = true) @@ -47,7 +47,7 @@ ethersuite "On-Chain Market": let config = await marketplace.configuration() hostRewardRecipient = accounts[2] - market = OnChainMarket.new(marketplace) + market = ! await OnChainMarket.load(marketplace) let tokenAddress = await marketplace.token() token = Erc20Token.new(tokenAddress, ethProvider.getSigner()) @@ -78,27 +78,22 @@ ethersuite "On-Chain Market": : await advanceToNextPeriod() - test "caches marketplace configuration": - check isNone market.configuration - discard await market.periodicity() - check isSome market.configuration - test "fails to instantiate when contract does not have a signer": let storageWithoutSigner = marketplace.connect(ethProvider) expect AssertionDefect: - discard OnChainMarket.new(storageWithoutSigner) + discard await OnChainMarket.load(storageWithoutSigner) test "knows signer address": check (await market.getSigner()) == (await ethProvider.getSigner().getAddress()) test "can retrieve proof periodicity": - let periodicity = await market.periodicity() + let periodicity = market.periodicity let config = await marketplace.configuration() let periodLength = config.proofs.period check periodicity.seconds == periodLength test "can retrieve proof timeout": - let proofTimeout = await market.proofTimeout() + let proofTimeout = market.proofTimeout let config = await marketplace.configuration() check proofTimeout == config.proofs.timeout @@ -256,9 +251,9 @@ ethersuite "On-Chain Market": await market.subscribeSlotReservationsFull(onSlotReservationsFull) await market.reserveSlot(request.id, slotIndex) - switchAccount(account2) + await switchAccount(account2) await market.reserveSlot(request.id, slotIndex) - switchAccount(account3) + await switchAccount(account3) await market.reserveSlot(request.id, slotIndex) check eventually receivedRequestIds == @[request.id] and receivedIdxs == @[ @@ -545,7 +540,7 @@ ethersuite "On-Chain Market": await market.requestStorage(request) let address = await host.getAddress() - switchAccount(host) + await switchAccount(host) await market.reserveSlot(request.id, 0.uint64) await market.fillSlot(request.id, 0.uint64, proof, request.ask.collateralPerSlot) let filledAt = await ethProvider.blockTime(BlockTag.latest) @@ -567,12 +562,12 @@ ethersuite "On-Chain Market": check endBalance == (startBalance + expectedPayout + request.ask.collateralPerSlot) test "pays rewards to reward recipient, collateral to host": - market = OnChainMarket.new(marketplace, hostRewardRecipient.some) + market = ! await OnChainMarket.load(marketplace, hostRewardRecipient.some) let hostAddress = await host.getAddress() await market.requestStorage(request) - switchAccount(host) + await switchAccount(host) await market.reserveSlot(request.id, 0.uint64) await market.fillSlot(request.id, 0.uint64, proof, request.ask.collateralPerSlot) let filledAt = (await ethProvider.currentTime())