from std/times import inMilliseconds, initDuration, inSeconds, fromUnix import std/sugar import pkg/codex/logutils import pkg/questionable/results import pkg/ethers/provider import ../contracts/time import ../contracts/deployment import ../codex/helpers import ../examples import ./marketplacesuite import ./nodeconfigs export logutils logScope: topics = "integration test validation" template eventuallyS( expression: untyped, timeout = 10, step = 5, cancelExpression: untyped = false ): bool = bind Moment, now, seconds proc eventuallyS(): Future[bool] {.async.} = let endTime = Moment.now() + timeout.seconds var secondsElapsed = 0 while not expression: if endTime < Moment.now(): return false if cancelExpression: return false await sleepAsync(step.seconds) return true await eventuallyS() marketplacesuite "Validation": let nodes = 3 let tolerance = 1 let proofProbability = 1 proc waitForRequestToFail( marketplace: Marketplace, requestId: RequestId, timeout = 10, step = 5 ): Future[bool] {.async.} = let endTime = Moment.now() + timeout.seconds var requestState = await marketplace.requestState(requestId) while requestState != RequestState.Failed: if endTime < Moment.now(): return false if requestState != RequestState.Started: return false await sleepAsync(step.seconds) requestState = await marketplace.requestState(requestId) return true test "validator marks proofs as missing when using validation groups", NodeConfigs( # Uncomment to start Hardhat automatically, typically so logs can be inspected locally hardhat: HardhatConfig.none, clients: CodexConfigs .init(nodes = 1) # .debug() # uncomment to enable console log output .withLogFile() # uncomment to output log file to tests/integration/logs/ //_.log .withLogTopics("purchases", "onchain").some, providers: CodexConfigs .init(nodes = 1) .withSimulateProofFailures(idx = 0, failEveryNProofs = 1) # .debug() # uncomment to enable console log output # .withLogFile() # uncomment to output log file to tests/integration/logs/ //_.log # .withLogTopics("sales", "onchain") .some, validators: CodexConfigs .init(nodes = 2) .withValidationGroups(groups = 2) .withValidationGroupIndex(idx = 0, groupIndex = 0) .withValidationGroupIndex(idx = 1, groupIndex = 1) # .debug() # uncomment to enable console log output .withLogFile() # uncomment to output log file to tests/integration/logs/ //_.log .withLogTopics("validator") # each topic as a separate string argument .some, ): let client0 = clients()[0].client let expiry = 5.periods let duration = expiry + 10.periods # let mine a block to sync the blocktime with the current clock discard await ethProvider.send("evm_mine") var currentTime = await ethProvider.currentTime() let requestEndTime = currentTime.truncate(uint64) + duration let data = await RandomChunker.example(blocks = 8) # TODO: better value for data.len below. This TODO is also present in # testproofs.nim - we may want to address it or remove the comment. createAvailabilities(data.len * 2, duration) let cid = client0.upload(data).get let purchaseId = await client0.requestStorage( cid, expiry = expiry, duration = duration, nodes = nodes, tolerance = tolerance, proofProbability = proofProbability, ) let requestId = client0.requestId(purchaseId).get debug "validation suite", purchaseId = purchaseId.toHex, requestId = requestId if not eventuallyS( client0.purchaseStateIs(purchaseId, "started"), timeout = (expiry + 60).int, step = 5, ): debug "validation suite: timed out waiting for the purchase to start" fail() return discard await ethProvider.send("evm_mine") currentTime = await ethProvider.currentTime() let secondsTillRequestEnd = (requestEndTime - currentTime.truncate(uint64)).int debug "validation suite", secondsTillRequestEnd = secondsTillRequestEnd.seconds check await marketplace.waitForRequestToFail( requestId, timeout = secondsTillRequestEnd + 60, step = 5 ) test "validator uses historical state to mark missing proofs", NodeConfigs( # Uncomment to start Hardhat automatically, typically so logs can be inspected locally hardhat: HardhatConfig.none, clients: CodexConfigs .init(nodes = 1) # .debug() # uncomment to enable console log output .withLogFile() # uncomment to output log file to tests/integration/logs/ //_.log .withLogTopics("purchases", "onchain").some, providers: CodexConfigs .init(nodes = 1) .withSimulateProofFailures(idx = 0, failEveryNProofs = 1) # .debug() # uncomment to enable console log output # .withLogFile() # uncomment to output log file to tests/integration/logs/ //_.log # .withLogTopics("sales", "onchain") .some, ): let client0 = clients()[0].client let expiry = 5.periods let duration = expiry + 10.periods # let mine a block to sync the blocktime with the current clock discard await ethProvider.send("evm_mine") var currentTime = await ethProvider.currentTime() let requestEndTime = currentTime.truncate(uint64) + duration let data = await RandomChunker.example(blocks = 8) # TODO: better value for data.len below. This TODO is also present in # testproofs.nim - we may want to address it or remove the comment. createAvailabilities(data.len * 2, duration) let cid = client0.upload(data).get let purchaseId = await client0.requestStorage( cid, expiry = expiry, duration = duration, nodes = nodes, tolerance = tolerance, proofProbability = proofProbability, ) let requestId = client0.requestId(purchaseId).get debug "validation suite", purchaseId = purchaseId.toHex, requestId = requestId if not eventuallyS( client0.purchaseStateIs(purchaseId, "started"), timeout = (expiry + 60).int, step = 5, ): debug "validation suite: timed out waiting for the purchase to start" fail() return # extra block just to make sure we have one that separates us # from the block containing the last (past) SlotFilled event discard await ethProvider.send("evm_mine") var validators = CodexConfigs .init(nodes = 2) .withValidationGroups(groups = 2) .withValidationGroupIndex(idx = 0, groupIndex = 0) .withValidationGroupIndex(idx = 1, groupIndex = 1) # .debug() # uncomment to enable console log output .withLogFile() # uncomment to output log file to: # tests/integration/logs/ //_.log .withLogTopics("validator") # each topic as a separate string argument failAndTeardownOnError "failed to start validator nodes": for config in validators.configs.mitems: let node = await startValidatorNode(config) running.add RunningNode(role: Role.Validator, node: node) discard await ethProvider.send("evm_mine") currentTime = await ethProvider.currentTime() let secondsTillRequestEnd = (requestEndTime - currentTime.truncate(uint64)).int debug "validation suite", secondsTillRequestEnd = secondsTillRequestEnd.seconds check await marketplace.waitForRequestToFail( requestId, timeout = secondsTillRequestEnd + 60, step = 5 )