Refactor and adjust test timeout durations

This commit is contained in:
Arnaud 2025-07-07 10:39:27 +02:00
parent 8f415be2c3
commit 5f8a6a9817
No known key found for this signature in database
GPG Key ID: B8FBC178F10CA7AE
8 changed files with 402 additions and 538 deletions

View File

@ -16,42 +16,29 @@ marketplacesuite(name = "Bug #821 - node crashes during erasure coding"):
providers: CodexConfigs.init(nodes = 0).some,
):
let
pricePerBytePerSecond = 1.u256
duration = 20.periods
collateralPerByte = 1.u256
expiry = 10.periods
data = await RandomChunker.example(blocks = 8)
client = clients()[0]
clientApi = client.client
data = await RandomChunker.example(blocks = 8)
let cid = (await clientApi.upload(data)).get
var requestId = none RequestId
proc onStorageRequested(eventResult: ?!StorageRequested) =
assert not eventResult.isErr
requestId = some (!eventResult).requestId
let subscription = await marketplace.subscribe(StorageRequested, onStorageRequested)
# client requests storage but requires multiple slots to host the content
let id = await clientApi.requestStorage(
cid,
duration = duration,
pricePerBytePerSecond = pricePerBytePerSecond,
expiry = expiry,
collateralPerByte = collateralPerByte,
nodes = 3,
tolerance = 1,
let (purchaseId, requestId) = await requestStorage(
clientApi, duration = duration, expiry = expiry, data = data.some
)
check eventually(requestId.isSome, timeout = expiry.int * 1000)
let storageRequestedEvent = newAsyncEvent()
proc onStorageRequested(eventResult: ?!StorageRequested) =
assert not eventResult.isErr
storageRequestedEvent.fire()
await marketplaceSubscribe(StorageRequested, onStorageRequested)
await storageRequestedEvent.wait().wait(timeout = chronos.seconds(expiry.int64))
let
request = await marketplace.getRequest(requestId.get)
request = await marketplace.getRequest(requestId)
cidFromRequest = request.content.cid
downloaded = await clientApi.downloadBytes(cidFromRequest, local = true)
check downloaded.isOk
check downloaded.get.toHex == data.toHex
await subscription.unsubscribe()

View File

@ -2,9 +2,10 @@ import std/times
import ../../examples
import ../../contracts/time
import ../../contracts/deployment
import ./../marketplacesuite
import ../twonodes
import ./../marketplacesuite except Subscription
import ../twonodes except Subscription
import ../nodeconfigs
from pkg/ethers import Subscription
marketplacesuite(name = "Marketplace"):
let marketplaceConfig = NodeConfigs(
@ -22,6 +23,11 @@ marketplacesuite(name = "Marketplace"):
const blocks = 8
const ecNodes = 3
const ecTolerance = 1
const size = 0xFFFFFF.uint64
const slotBytes = slotSize(blocks, ecNodes, ecTolerance)
const duration = 20 * 60.uint64
const expiry = 10 * 60.uint64
const pricePerSlotPerSecond = minPricePerBytePerSecond * slotBytes
setup:
host = providers()[0].client
@ -35,45 +41,33 @@ marketplacesuite(name = "Marketplace"):
await ethProvider.advanceTime(1.u256)
test "nodes negotiate contracts on the marketplace", marketplaceConfig:
let size = 0xFFFFFF.uint64
let data = await RandomChunker.example(blocks = blocks)
# host makes storage available
let availability = (
await host.postAvailability(
totalSize = size,
duration = 20 * 60.uint64,
duration = duration,
minPricePerBytePerSecond = minPricePerBytePerSecond,
totalCollateral = size.u256 * minPricePerBytePerSecond,
)
).get
# client requests storage
let cid = (await client.upload(data)).get
let id = await client.requestStorage(
cid,
duration = 20 * 60.uint64,
pricePerBytePerSecond = minPricePerBytePerSecond,
proofProbability = 3.u256,
expiry = 10 * 60.uint64,
collateralPerByte = collateralPerByte,
nodes = ecNodes,
tolerance = ecTolerance,
)
let (purchaseId, requestId) = await requestStorage(client)
discard await waitForRequestToStart()
await waitForRequestToStart(requestId, expiry.int64)
let purchase = (await client.getPurchase(id)).get
let purchase = (await client.getPurchase(purchaseId)).get
check purchase.error == none string
let state = await marketplace.requestState(purchase.requestId)
let state = await marketplace.requestState(requestId)
check state == RequestState.Started
let availabilities = (await host.getAvailabilities()).get
check availabilities.len == 1
let datasetSize = datasetSize(blocks, ecNodes, ecTolerance)
let newSize = availabilities[0].freeSize
check newSize > 0 and newSize + datasetSize == size
let datasetSize = datasetSize(blocks, ecNodes, ecTolerance)
check newSize > 0 and newSize.u256 + datasetSize == size.u256
let reservations = (await host.getAvailabilityReservations(availability.id)).get
check reservations.len == 3
@ -90,46 +84,25 @@ marketplacesuite(name = "Marketplace"):
test "node slots gets paid out and rest of tokens are returned to client",
marketplaceConfig:
let size = 0xFFFFFF.uint64
let data = await RandomChunker.example(blocks = blocks)
let marketplace = Marketplace.new(Marketplace.address, ethProvider.getSigner())
let tokenAddress = await marketplace.token()
let token = Erc20Token.new(tokenAddress, ethProvider.getSigner())
let duration = 20 * 60.uint64
var providerRewardEvent = newAsyncEvent()
var clientFundsEvent = newAsyncEvent()
var transferEvent = newAsyncEvent()
var filledAtPerSlot: seq[UInt256] = @[]
var requestId: RequestId
# host makes storage available
let startBalanceHost = await token.balanceOf(hostAccount)
let startBalanceClient = await token.balanceOf(clientAccount)
discard (
await host.postAvailability(
totalSize = size,
duration = 20 * 60.uint64,
minPricePerBytePerSecond = minPricePerBytePerSecond,
totalCollateral = size.u256 * minPricePerBytePerSecond,
)
).get
let slotSize = slotSize(blocks, ecNodes, ecTolerance)
var filledAtPerSlot: seq[UInt256] = @[]
proc storeFilledAtTimestamps() {.async.} =
let filledAt = await ethProvider.blockTime(BlockTag.latest)
filledAtPerSlot.add(filledAt)
proc onSlotFilled(eventResult: ?!SlotFilled) =
proc onSlotFilled(eventResult: ?!SlotFilled) {.raises: [].} =
assert not eventResult.isErr
let event = !eventResult
asyncSpawn storeFilledAtTimestamps()
let filledSubscription = await marketplace.subscribe(SlotFilled, onSlotFilled)
# client requests storage
let cid = (await client.upload(data)).get
let pricePerSlotPerSecond = minPricePerBytePerSecond * slotSize
var providerRewardEvent = newAsyncEvent()
proc checkProviderRewards() {.async.} =
let endBalanceHost = await token.balanceOf(hostAccount)
let requestEnd = await marketplace.requestEnd(requestId)
@ -140,23 +113,19 @@ marketplacesuite(name = "Marketplace"):
if rewards + startBalanceHost == endBalanceHost:
providerRewardEvent.fire()
var clientFundsEvent = newAsyncEvent()
proc checkClientFunds() {.async.} =
let requestEnd = await marketplace.requestEnd(requestId)
let hostRewards = filledAtPerSlot
.mapIt((requestEnd.u256 - it) * pricePerSlotPerSecond)
.foldl(a + b, 0.u256)
proc checkHostFunds() {.async.} =
var hostRewards = 0.u256
for filledAt in filledAtPerSlot:
hostRewards += (requestEnd.u256 - filledAt) * pricePerSlotPerSecond
let requestPrice = minPricePerBytePerSecond * slotSize * duration.u256 * 3
let requestPrice = pricePerSlotPerSecond * duration.u256 * 3
let fundsBackToClient = requestPrice - hostRewards
let endBalanceClient = await token.balanceOf(clientAccount)
if startBalanceClient + fundsBackToClient - requestPrice == endBalanceClient:
clientFundsEvent.fire()
var transferEvent = newAsyncEvent()
proc onTransfer(eventResult: ?!Transfer) =
assert not eventResult.isErr
@ -164,36 +133,39 @@ marketplacesuite(name = "Marketplace"):
if data.receiver == hostAccount:
asyncSpawn checkProviderRewards()
if data.receiver == clientAccount:
asyncSpawn checkHostFunds()
asyncSpawn checkClientFunds()
let id = await client.requestStorage(
cid,
duration = duration,
pricePerBytePerSecond = minPricePerBytePerSecond,
proofProbability = 3.u256,
expiry = 10 * 60.uint64,
collateralPerByte = collateralPerByte,
nodes = ecNodes,
tolerance = ecTolerance,
)
discard (
await host.postAvailability(
totalSize = size,
duration = duration,
minPricePerBytePerSecond = minPricePerBytePerSecond,
totalCollateral = size.u256 * minPricePerBytePerSecond,
)
).get
let requestId = (await client.requestId(id)).get
# client requests storage
let (_, id) = await requestStorage(client)
requestId = id
discard await waitForRequestToStart()
# Subscribe SlotFilled event to receive the filledAt timestamp
# and calculate the provider reward
await marketplaceSubscribe(SlotFilled, onSlotFilled)
stopOnRequestFailed:
# Proving mechanism uses blockchain clock to do proving/collect/cleanup round
# hence we must use `advanceTime` over `sleepAsync` as Hardhat does mine new blocks
# only with new transaction
await ethProvider.advanceTime(duration.u256)
await waitForRequestToStart(requestId, expiry.int64)
let tokenSubscription = await token.subscribe(Transfer, onTransfer)
# Proving mechanism uses blockchain clock to do proving/collect/cleanup round
# hence we must use `advanceTime` over `sleepAsync` as Hardhat does mine new blocks
# only with new transaction
await ethProvider.advanceTime(duration.u256)
await clientFundsEvent.wait().wait(timeout = chronos.seconds(60))
await providerRewardEvent.wait().wait(timeout = chronos.seconds(60))
await tokenSubscribe(onTransfer)
await tokenSubscription.unsubscribe()
await filledSubscription.unsubscribe()
# Wait for the exact expected balances.
# The timeout is 60 seconds because the event should occur quickly,
# thanks to `advanceTime` moving to the end of the request duration.
await clientFundsEvent.wait().wait(timeout = chronos.seconds(60))
await providerRewardEvent.wait().wait(timeout = chronos.seconds(60))
test "SP are able to process slots after workers were busy with other slots and ignored them",
NodeConfigs(
@ -206,76 +178,52 @@ marketplacesuite(name = "Marketplace"):
# .withLogTopics("marketplace", "sales", "statemachine","slotqueue", "reservations")
.some,
):
let client0 = clients()[0]
let provider0 = providers()[0]
let provider1 = providers()[1]
let duration = 20 * 60.uint64
let data = await RandomChunker.example(blocks = blocks)
let slotSize = slotSize(blocks, ecNodes, ecTolerance)
var requestId: RequestId
# We create an avavilability allowing the first SP to host the 3 slots.
# So the second SP will not have any availability so it will just process
# the slots and ignore them.
discard await provider0.client.postAvailability(
totalSize = 3 * slotSize.truncate(uint64),
discard await host.postAvailability(
totalSize = 3 * slotBytes.truncate(uint64),
duration = duration,
minPricePerBytePerSecond = minPricePerBytePerSecond,
totalCollateral = 3 * slotSize * minPricePerBytePerSecond,
totalCollateral = 3 * slotBytes * minPricePerBytePerSecond,
)
let cid = (await client0.client.upload(data)).get
let purchaseId = await client0.client.requestStorage(
cid,
duration = duration,
pricePerBytePerSecond = minPricePerBytePerSecond,
proofProbability = 1.u256,
expiry = 10 * 60.uint64,
collateralPerByte = collateralPerByte,
nodes = ecNodes,
tolerance = ecTolerance,
)
let requestId = (await client0.client.requestId(purchaseId)).get
let (_, id) = await requestStorage(client)
requestId = id
# We wait that the 3 slots are filled by the first SP
discard await waitForRequestToStart()
await waitForRequestToStart(requestId, expiry.int64)
# Here we create the same availability as previously but for the second SP.
# Meaning that, after ignoring all the slots for the first request, the second SP will process
# and host the slots for the second request.
discard await provider1.client.postAvailability(
totalSize = 3 * slotSize.truncate(uint64),
let host1 = providers()[1].client
discard await host1.postAvailability(
totalSize = 3 * slotBytes.truncate(uint64),
duration = duration,
minPricePerBytePerSecond = minPricePerBytePerSecond,
totalCollateral = 3 * slotSize * collateralPerByte,
totalCollateral = 3 * slotBytes * collateralPerByte,
)
let purchaseId2 = await client0.client.requestStorage(
cid,
duration = duration,
pricePerBytePerSecond = minPricePerBytePerSecond,
proofProbability = 3.u256,
expiry = 10 * 60.uint64,
collateralPerByte = collateralPerByte,
nodes = ecNodes,
tolerance = ecTolerance,
)
let requestId2 = (await client0.client.requestId(purchaseId2)).get
let (_, id2) = await requestStorage(client)
requestId = id2
# Wait that the slots of the second request are filled
discard await waitForRequestToStart()
await waitForRequestToStart(requestId, expiry.int64)
# Double check, verify that our second SP hosts the 3 slots
let signer = ethProvider.getSigner(hostAccount)
let host1Account = providers()[1].ethAccount
let signer = ethProvider.getSigner(host1Account)
let marketplaceWithProviderSigner = marketplace.connect(signer)
let slots = await marketplaceWithProviderSigner.mySlots()
check slots.len == 3
for slotId in slots:
let slot = await marketplaceWithProviderSigner.getActiveSlot(slotId)
check slot.request.id == purchase.requestId
check slot.request.id == requestId
marketplacesuite(name = "Marketplace payouts"):
const minPricePerBytePerSecond = 1.u256
@ -283,6 +231,10 @@ marketplacesuite(name = "Marketplace payouts"):
const blocks = 8
const ecNodes = 3
const ecTolerance = 1
const slotBytes = slotSize(blocks, ecNodes, ecTolerance)
const duration = 20 * 60.uint64
const expiry = 10 * 60.uint64
const pricePerSlotPerSecond = minPricePerBytePerSecond * slotBytes
test "expired request partially pays out for stored time",
NodeConfigs(
@ -303,85 +255,46 @@ marketplacesuite(name = "Marketplace payouts"):
# )
.some,
):
let duration = 20.periods
let expiry = 10.periods
let data = await RandomChunker.example(blocks = blocks)
let client = clients()[0]
let provider = providers()[0]
let clientApi = client.client
let providerApi = provider.client
let startBalanceProvider = await token.balanceOf(provider.ethAccount)
let startBalanceClient = await token.balanceOf(client.ethAccount)
let hostAccount = providers()[0].ethAccount
let clientAccount = clients()[0].ethAccount
# provider makes storage available
let datasetSize = datasetSize(blocks, ecNodes, ecTolerance)
let totalAvailabilitySize = (datasetSize div 2).truncate(uint64)
discard await providerApi.postAvailability(
# make availability size small enough that we can't fill all the slots,
# thus causing a cancellation
totalSize = totalAvailabilitySize,
duration = duration.uint64,
minPricePerBytePerSecond = minPricePerBytePerSecond,
totalCollateral = collateralPerByte * totalAvailabilitySize.u256,
)
let cid = (await clientApi.upload(data)).get
var filledAtPerSlot: seq[UInt256] = @[]
var slotIndex = 0.uint64
var slotFilledEvent = newAsyncEvent()
var requestCancelledEvent = newAsyncEvent()
var providerRewardEvent = newAsyncEvent()
var filledAtPerSlot: seq[UInt256] = @[]
var requestId: RequestId
let startBalanceClient = await token.balanceOf(client.ethAccount)
let startBalanceProvider = await token.balanceOf(hostAccount)
proc storeFilledAtTimestamps() {.async.} =
let filledAt = await ethProvider.blockTime(BlockTag.latest)
filledAtPerSlot.add(filledAt)
slotFilledEvent.fire()
proc onSlotFilled(eventResult: ?!SlotFilled) =
proc onSlotFilled(eventResult: ?!SlotFilled) {.raises: [].} =
assert not eventResult.isErr
let event = !eventResult
slotIndex = event.slotIndex
asyncSpawn storeFilledAtTimestamps()
slotFilledEvent.fire()
let slotFilledSubscription = await marketplace.subscribe(SlotFilled, onSlotFilled)
var requestCancelledEvent = newAsyncEvent()
proc onRequestCancelled(eventResult: ?!RequestCancelled) =
assert not eventResult.isErr
requestCancelledEvent.fire()
let requestCancelledSubscription =
await marketplace.subscribe(RequestCancelled, onRequestCancelled)
# client requests storage but requires multiple slots to host the content
let id = await clientApi.requestStorage(
cid,
duration = duration,
pricePerBytePerSecond = minPricePerBytePerSecond,
expiry = expiry,
collateralPerByte = collateralPerByte,
nodes = ecNodes,
tolerance = ecTolerance,
)
let requestId = (await clientApi.requestId(id)).get
# wait until one slot is filled
await slotFilledEvent.wait().wait(timeout = chronos.seconds(expiry.int))
let slotId = slotId(!(await clientApi.requestId(id)), slotIndex)
let slotSize = slotSize(blocks, ecNodes, ecTolerance)
let pricePerSlotPerSecond = minPricePerBytePerSecond * slotSize
var providerRewardEvent = newAsyncEvent()
proc checkProviderRewards() {.async.} =
let endBalanceProvider = await token.balanceOf(hostAccount)
let requestEnd = await marketplace.requestEnd(requestId)
let rewards = filledAtPerSlot
.mapIt((requestEnd.u256 - it) * pricePerSlotPerSecond)
.foldl(a + b, 0.u256)
let endBalanceHost = await token.balanceOf(hostAccount)
if rewards + startBalanceProvider == endBalanceHost:
if rewards + startBalanceProvider == endBalanceProvider:
providerRewardEvent.fire()
proc onTransfer(eventResult: ?!Transfer) =
@ -391,15 +304,37 @@ marketplacesuite(name = "Marketplace payouts"):
if data.receiver == hostAccount:
asyncSpawn checkProviderRewards()
let tokenAddress = await marketplace.token()
let token = Erc20Token.new(tokenAddress, ethProvider.getSigner())
let tokenSubscription = await token.subscribe(Transfer, onTransfer)
# provider makes storage available
let datasetSize = datasetSize(blocks, ecNodes, ecTolerance)
let totalAvailabilitySize = (datasetSize div 2).truncate(uint64)
discard await providerApi.postAvailability(
# make availability size small enough that we can't fill all the slots,
# thus causing a cancellation
totalSize = totalAvailabilitySize,
duration = duration,
minPricePerBytePerSecond = minPricePerBytePerSecond,
totalCollateral = collateralPerByte * totalAvailabilitySize.u256,
)
let (_, id) = await requestStorage(clientApi)
requestId = id
await marketplaceSubscribe(SlotFilled, onSlotFilled)
await marketplaceSubscribe(RequestCancelled, onRequestCancelled)
# wait until one slot is filled
await slotFilledEvent.wait().wait(timeout = chronos.seconds(expiry.int))
let slotId = slotId(requestId, slotIndex)
await tokenSubscribe(onTransfer)
# wait until sale is cancelled
await ethProvider.advanceTime(expiry.u256)
await requestCancelledEvent.wait().wait(timeout = chronos.seconds(5))
await advanceToNextPeriod()
# Wait for the expected balance for the provider
await providerRewardEvent.wait().wait(timeout = chronos.seconds(60))
# Ensure that total rewards stay within the payout limit
@ -412,14 +347,11 @@ marketplacesuite(name = "Marketplace payouts"):
let endBalanceProvider = (await token.balanceOf(provider.ethAccount))
let endBalanceClient = (await token.balanceOf(client.ethAccount))
check(
startBalanceClient - endBalanceClient == endBalanceProvider - startBalanceProvider
)
await slotFilledSubscription.unsubscribe()
await requestCancelledSubscription.unsubscribe()
await tokenSubscription.unsubscribe()
test "the collateral is returned after a sale is ignored",
NodeConfigs(
hardhat: HardhatConfig.none,
@ -434,13 +366,10 @@ marketplacesuite(name = "Marketplace payouts"):
# )
.some,
):
let data = await RandomChunker.example(blocks = blocks)
let client0 = clients()[0]
let provider0 = providers()[0]
let provider1 = providers()[1]
let provider2 = providers()[2]
let duration = 20 * 60.uint64
let slotSize = slotSize(blocks, ecNodes, ecTolerance)
# Here we create 3 SP which can host 3 slot.
# While they will process the slot, each SP will
@ -449,59 +378,44 @@ marketplacesuite(name = "Marketplace payouts"):
# will be ignored. In that case, the collateral assigned for
# the reservation should return to the availability.
discard await provider0.client.postAvailability(
totalSize = 3 * slotSize.truncate(uint64),
totalSize = 3 * slotBytes.truncate(uint64),
duration = duration,
minPricePerBytePerSecond = minPricePerBytePerSecond,
totalCollateral = 3 * slotSize * minPricePerBytePerSecond,
totalCollateral = 3 * slotBytes * minPricePerBytePerSecond,
)
discard await provider1.client.postAvailability(
totalSize = 3 * slotSize.truncate(uint64),
totalSize = 3 * slotBytes.truncate(uint64),
duration = duration,
minPricePerBytePerSecond = minPricePerBytePerSecond,
totalCollateral = 3 * slotSize * minPricePerBytePerSecond,
totalCollateral = 3 * slotBytes * minPricePerBytePerSecond,
)
discard await provider2.client.postAvailability(
totalSize = 3 * slotSize.truncate(uint64),
totalSize = 3 * slotBytes.truncate(uint64),
duration = duration,
minPricePerBytePerSecond = minPricePerBytePerSecond,
totalCollateral = 3 * slotSize * minPricePerBytePerSecond,
totalCollateral = 3 * slotBytes * minPricePerBytePerSecond,
)
let cid = (await client0.client.upload(data)).get
let (_, requestId) = await requestStorage(client0.client)
await waitForRequestToStart(requestId, expiry.int64)
let purchaseId = await client0.client.requestStorage(
cid,
duration = duration,
pricePerBytePerSecond = minPricePerBytePerSecond,
proofProbability = 1.u256,
expiry = 10 * 60.uint64,
collateralPerByte = collateralPerByte,
nodes = ecNodes,
tolerance = ecTolerance,
)
# Here we will check that for each provider, the total remaining collateral
# will match the available slots.
# So if a SP hosts 1 slot, it should have enough total remaining collateral
# to host 2 more slots.
for provider in providers():
let client = provider.client
check eventually(
block:
try:
let availabilities = (await client.getAvailabilities()).get
let availability = availabilities[0]
let slots = (await client.getSlots()).get
let availableSlots = (3 - slots.len).u256
let requestId = (await client0.client.requestId(purchaseId)).get
discard await waitForRequestToStart()
stopOnRequestFailed:
# Here we will check that for each provider, the total remaining collateral
# will match the available slots.
# So if a SP hosts 1 slot, it should have enough total remaining collateral
# to host 2 more slots.
for provider in providers():
let client = provider.client
check eventually(
block:
try:
let availabilities = (await client.getAvailabilities()).get
let availability = availabilities[0]
let slots = (await client.getSlots()).get
let availableSlots = (3 - slots.len).u256
availability.totalRemainingCollateral ==
availableSlots * slotSize * minPricePerBytePerSecond
except HttpConnectionError:
return false,
timeout = 30 * 1000,
)
availability.totalRemainingCollateral ==
availableSlots * slotBytes * minPricePerBytePerSecond
except HttpConnectionError:
return false,
timeout = 30 * 1000,
)

View File

@ -38,8 +38,12 @@ marketplacesuite(name = "Hosts submit regular proofs"):
let client0 = clients()[0].client
let expiry = 10.periods
let duration = expiry + 5.periods
let proofSubmittedEvent = newAsyncEvent()
proc onProofSubmitted(event: ?!ProofSubmitted) =
if event.isOk:
proofSubmittedEvent.fire()
let data = await RandomChunker.example(blocks = blocks)
let datasetSize =
datasetSize(blocks = blocks, nodes = ecNodes, tolerance = ecTolerance)
await createAvailabilities(
@ -49,32 +53,20 @@ marketplacesuite(name = "Hosts submit regular proofs"):
minPricePerBytePerSecond,
)
let cid = (await client0.upload(data)).get
let purchaseId = await client0.requestStorage(
cid,
expiry = expiry,
duration = duration,
nodes = ecNodes,
tolerance = ecTolerance,
)
let (purchaseId, requestId) =
await requestStorage(client0, duration = duration, expiry = expiry)
let purchase = (await client0.getPurchase(purchaseId)).get
check purchase.error == none string
let slotSize = slotSize(blocks, ecNodes, ecTolerance)
await marketplaceSubscribe(ProofSubmitted, onProofSubmitted)
discard await waitForRequestToStart(expiry.int)
await waitForRequestToStart(requestId, expiry.int64)
var proofWasSubmitted = false
proc onProofSubmitted(event: ?!ProofSubmitted) =
proofWasSubmitted = event.isOk
let subscription = await marketplace.subscribe(ProofSubmitted, onProofSubmitted)
check eventually(proofWasSubmitted, timeout = (duration - expiry).int * 1000)
await subscription.unsubscribe()
let secondsTillRequestEnd = await getSecondsTillRequestEnd(requestId)
await proofSubmittedEvent.wait().wait(
timeout = chronos.seconds(secondsTillRequestEnd)
)
marketplacesuite(name = "Simulate invalid proofs"):
# TODO: these are very loose tests in that they are not testing EXACTLY how
@ -88,6 +80,17 @@ marketplacesuite(name = "Simulate invalid proofs"):
const ecNodes = 3
const ecTolerance = 1
var slotWasFreedEvent: AsyncEvent
var requestId: RequestId
proc onSlotFreed(event: ?!SlotFreed) =
if event.isOk and event.value.requestId == requestId:
slotWasFreedEvent.fire()
setup:
requestId = RequestId.default()
slotWasFreedEvent = newAsyncEvent()
test "slot is freed after too many invalid proofs submitted",
NodeConfigs(
# Uncomment to start Hardhat automatically, typically so logs can be inspected locally
@ -120,7 +123,6 @@ marketplacesuite(name = "Simulate invalid proofs"):
let expiry = 10.periods
let duration = expiry + 10.periods
let data = await RandomChunker.example(blocks = blocks)
let datasetSize =
datasetSize(blocks = blocks, nodes = ecNodes, tolerance = ecTolerance)
await createAvailabilities(
@ -130,32 +132,14 @@ marketplacesuite(name = "Simulate invalid proofs"):
minPricePerBytePerSecond,
)
let cid = (await client0.upload(data)).get
let purchaseId = (
await client0.requestStorage(
cid,
expiry = expiry,
duration = duration,
nodes = ecNodes,
tolerance = ecTolerance,
proofProbability = 1.u256,
)
let (purchaseId, id) = await requestStorage(
client0, duration = duration, proofProbability = 1.u256, expiry = expiry
)
let requestId = (await client0.requestId(purchaseId)).get
requestId = id
discard await waitForRequestToStart(expiry.int)
await marketplaceSubscribe(SlotFreed, onSlotFreed)
var slotWasFreed = false
proc onSlotFreed(event: ?!SlotFreed) =
if event.isOk and event.value.requestId == requestId:
slotWasFreed = true
let subscription = await marketplace.subscribe(SlotFreed, onSlotFreed)
check eventually(slotWasFreed, timeout = (duration - expiry).int * 1000)
await subscription.unsubscribe()
await slotWasFreedEvent.wait().wait(timeout = chronos.seconds(duration.int64))
test "slot is not freed when not enough invalid proofs submitted",
NodeConfigs(
@ -182,8 +166,15 @@ marketplacesuite(name = "Simulate invalid proofs"):
let client0 = clients()[0].client
let expiry = 10.periods
let duration = expiry + 10.periods
let slotWasFilledEvent = newAsyncEvent()
proc onSlotFilled(eventResult: ?!SlotFilled) =
assert not eventResult.isErr
let event = !eventResult
if event.requestId == requestId:
slotWasFilledEvent.fire()
let data = await RandomChunker.example(blocks = blocks)
let datasetSize =
datasetSize(blocks = blocks, nodes = ecNodes, tolerance = ecTolerance)
await createAvailabilities(
@ -193,44 +184,24 @@ marketplacesuite(name = "Simulate invalid proofs"):
minPricePerBytePerSecond,
)
let cid = (await client0.upload(data)).get
let (purchaseId, id) =
await requestStorage(client0, duration = duration, expiry = expiry)
requestId = id
let purchaseId = await client0.requestStorage(
cid,
expiry = expiry,
duration = duration,
nodes = ecNodes,
tolerance = ecTolerance,
proofProbability = 1.u256,
)
let requestId = (await client0.requestId(purchaseId)).get
var slotWasFilled = false
proc onSlotFilled(eventResult: ?!SlotFilled) =
assert not eventResult.isErr
let event = !eventResult
if event.requestId == requestId:
slotWasFilled = true
let filledSubscription = await marketplace.subscribe(SlotFilled, onSlotFilled)
await marketplaceSubscribe(SlotFilled, onSlotFilled)
await marketplaceSubscribe(SlotFreed, onSlotFreed)
# wait for the first slot to be filled
check eventually(slotWasFilled, timeout = expiry.int * 1000)
await slotWasFilledEvent.wait().wait(timeout = chronos.seconds(expiry.int64))
var slotWasFreed = false
proc onSlotFreed(event: ?!SlotFreed) =
if event.isOk and event.value.requestId == requestId:
slotWasFreed = true
let freedSubscription = await marketplace.subscribe(SlotFreed, onSlotFreed)
# In 2 periods you cannot have enough invalid proofs submitted:
await sleepAsync(2.periods.int.seconds)
check not slotWasFreed
await filledSubscription.unsubscribe()
await freedSubscription.unsubscribe()
try:
# In 2 periods you cannot have enough invalid proofs submitted:
await slotWasFreedEvent.wait().wait(
timeout = chronos.seconds(2.periods.int.seconds)
)
fail("invalid proofs were not expected in 2 periods")
except AsyncTimeoutError:
discard
# TODO: uncomment once fixed
# WARNING: in the meantime minPrice has changed to minPricePerBytePerSecond

View File

@ -20,18 +20,17 @@ marketplacesuite(name = "SP Slot Repair"):
const ecTolerance = 1
const size = slotSize(blocks, ecNodes, ecTolerance)
var filledSlotIds: seq[SlotId] = @[]
var freedSlotId = none SlotId
var freedSlotIndex = none uint64
var requestId: RequestId
var slotFilledEvent: AsyncEvent
# Here we are keeping track of the slot filled using their ids.
proc onSlotFilled(eventResult: ?!SlotFilled) =
assert not eventResult.isErr
let event = !eventResult
if event.requestId == requestId:
let slotId = slotId(event.requestId, event.slotIndex)
filledSlotIds.add slotId
if event.requestId == requestId and event.slotIndex == freedSlotIndex.get:
slotFilledEvent.fire()
# Here we are retrieving the slot id freed.
# When the event is triggered, the slot id is removed
@ -42,44 +41,23 @@ marketplacesuite(name = "SP Slot Repair"):
let slotId = slotId(event.requestId, event.slotIndex)
if event.requestId == requestId:
assert slotId in filledSlotIds
filledSlotIds.del(filledSlotIds.find(slotId))
freedSlotId = some(slotId)
freedSlotIndex = some event.slotIndex
proc createPurchase(client: CodexClient): Future[PurchaseId] {.async.} =
let data = await RandomChunker.example(blocks = blocks)
let cid = (await client.upload(data)).get
let purchaseId = await client.requestStorage(
cid,
expiry = 10.periods,
duration = 20.periods,
nodes = ecNodes,
tolerance = ecTolerance,
collateralPerByte = 1.u256,
pricePerBytePerSecond = minPricePerBytePerSecond,
proofProbability = 1.u256,
)
requestId = (await client.requestId(purchaseId)).get
return purchaseId
proc freeSlot(provider: CodexClient): Future[void] {.async.} =
proc freeSlot(provider: CodexProcess): Future[void] {.async.} =
# Get the second provider signer.
let signer = ethProvider.getSigner(accounts[2])
let signer = ethProvider.getSigner(provider.ethAccount)
let marketplaceWithSecondProviderSigner = marketplace.connect(signer)
# Call freeSlot to speed up the process.
# It accelerates the test by skipping validator
# proof verification and not waiting for the full period.
# The downside is that this doesn't reflect the real slot freed process.
let slots = (await provider.getSlots()).get()
let slots = (await provider.client.getSlots()).get()
let slotId = slotId(requestId, slots[0].slotIndex)
discard await marketplaceWithSecondProviderSigner.freeSlot(slotId)
setup:
filledSlotIds = @[]
freedSlotId = none SlotId
slotFilledEvent = newAsyncEvent()
test "repair from local store",
NodeConfigs(
@ -124,13 +102,17 @@ marketplacesuite(name = "SP Slot Repair"):
)
).get
let purchaseId = await createPurchase(client0.client)
let filledSubscription = await marketplace.subscribe(SlotFilled, onSlotFilled)
let slotFreedsubscription = await marketplace.subscribe(SlotFreed, onSlotFreed)
let (purchaseId, id) = await requestStorage(
client0.client,
blocks = blocks,
expiry = expiry,
duration = duration,
proofProbability = 1.u256,
)
requestId = id
# Wait for purchase starts, meaning that the slots are filled.
discard await waitForRequestToStart(expiry.int)
await waitForRequestToStart(requestId, expiry.int64)
# stop client so it doesn't serve any blocks anymore
await client0.stop()
@ -148,17 +130,16 @@ marketplacesuite(name = "SP Slot Repair"):
totalSize = (3 * size.truncate(uint64)).uint64.some,
)
await marketplaceSubscribe(SlotFilled, onSlotFilled)
await marketplaceSubscribe(SlotFreed, onSlotFreed)
# Let's free the slot to speed up the process
await freeSlot(provider1.client)
await freeSlot(provider1)
# We expect that the freed slot is added in the filled slot id list,
# We expect that the freed slot is filled again,
# meaning that the slot was repaired locally by SP 1.
# check eventually(
# freedSlotId.get in filledSlotIds, timeout = (duration - expiry).int * 1000
# )
await filledSubscription.unsubscribe()
await slotFreedsubscription.unsubscribe()
let secondsTillRequestEnd = await getSecondsTillRequestEnd(requestId)
await slotFilledEvent.wait().wait(timeout = chronos.seconds(secondsTillRequestEnd))
test "repair from local and remote store",
NodeConfigs(
@ -204,13 +185,17 @@ marketplacesuite(name = "SP Slot Repair"):
totalCollateral = size * collateralPerByte,
)
let purchaseId = await createPurchase(client0.client)
let filledSubscription = await marketplace.subscribe(SlotFilled, onSlotFilled)
let slotFreedsubscription = await marketplace.subscribe(SlotFreed, onSlotFreed)
let (purchaseId, id) = await requestStorage(
client0.client,
blocks = blocks,
expiry = expiry,
duration = duration,
proofProbability = 1.u256,
)
requestId = id
# Wait for purchase starts, meaning that the slots are filled.
discard await waitForRequestToStart(expiry.int)
await waitForRequestToStart(requestId, expiry.int64)
# stop client so it doesn't serve any blocks anymore
await client0.stop()
@ -227,16 +212,16 @@ marketplacesuite(name = "SP Slot Repair"):
totalCollateral = (2 * size * collateralPerByte).some,
)
await marketplaceSubscribe(SlotFilled, onSlotFilled)
await marketplaceSubscribe(SlotFreed, onSlotFreed)
# Let's free the slot to speed up the process
await freeSlot(provider1.client)
await freeSlot(provider1)
# We expect that the freed slot is added in the filled slot id list,
# We expect that the freed slot is filled again,
# meaning that the slot was repaired locally and remotely (using SP 3) by SP 1.
# check eventually(freedSlotId.isSome, timeout = expiry.int * 1000)
# check eventually(freedSlotId.get in filledSlotIds, timeout = expiry.int * 1000)
await filledSubscription.unsubscribe()
await slotFreedsubscription.unsubscribe()
let secondsTillRequestEnd = await getSecondsTillRequestEnd(requestId)
await slotFilledEvent.wait().wait(timeout = chronos.seconds(secondsTillRequestEnd))
test "repair from remote store only",
NodeConfigs(
@ -275,13 +260,17 @@ marketplacesuite(name = "SP Slot Repair"):
)
).get
let purchaseId = await createPurchase(client0.client)
let filledSubscription = await marketplace.subscribe(SlotFilled, onSlotFilled)
let slotFreedsubscription = await marketplace.subscribe(SlotFreed, onSlotFreed)
let (purchaseId, id) = await requestStorage(
client0.client,
blocks = blocks,
expiry = expiry,
duration = duration,
proofProbability = 1.u256,
)
requestId = id
# Wait for purchase starts, meaning that the slots are filled.
discard await waitForRequestToStart(expiry.int)
await waitForRequestToStart(requestId, expiry.int64)
# stop client so it doesn't serve any blocks anymore
await client0.stop()
@ -299,12 +288,12 @@ marketplacesuite(name = "SP Slot Repair"):
# SP 2 will not pick the slot again.
await provider1.client.patchAvailability(availability1.id, enabled = false.some)
await marketplaceSubscribe(SlotFilled, onSlotFilled)
await marketplaceSubscribe(SlotFreed, onSlotFreed)
# Let's free the slot to speed up the process
await freeSlot(provider1.client)
await freeSlot(provider1)
# At this point, SP 3 should repair the slot from SP 1 and host it.
# check eventually(freedSlotId.isSome, timeout = expiry.int * 1000)
# check eventually(freedSlotId.get in filledSlotIds, timeout = expiry.int * 1000)
await filledSubscription.unsubscribe()
await slotFreedsubscription.unsubscribe()
let secondsTillRequestEnd = await getSecondsTillRequestEnd(requestId)
await slotFilledEvent.wait().wait(timeout = chronos.seconds(secondsTillRequestEnd))

View File

@ -20,7 +20,6 @@ marketplacesuite(name = "Validation"):
const ecNodes = 3
const ecTolerance = 1
const proofProbability = 1.u256
const collateralPerByte = 1.u256
const minPricePerBytePerSecond = 1.u256
@ -60,9 +59,6 @@ marketplacesuite(name = "Validation"):
# 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 = blocks)
let datasetSize =
datasetSize(blocks = blocks, nodes = ecNodes, tolerance = ecTolerance)
@ -73,28 +69,19 @@ marketplacesuite(name = "Validation"):
minPricePerBytePerSecond,
)
let cid = (await client0.upload(data)).get
let purchaseId = await client0.requestStorage(
cid,
expiry = expiry,
duration = duration,
nodes = ecNodes,
tolerance = ecTolerance,
proofProbability = proofProbability,
let (purchaseId, requestId) = await requestStorage(
client0, duration = duration, expiry = expiry, proofProbability = proofProbability
)
let requestId = (await client0.requestId(purchaseId)).get
debug "validation suite", purchaseId = purchaseId.toHex, requestId = requestId
discard await waitForRequestToStart(expiry.int + 60)
await waitForRequestToStart(requestId, expiry.int64)
discard await ethProvider.send("evm_mine")
currentTime = await ethProvider.currentTime()
let secondsTillRequestEnd = (requestEndTime - currentTime.truncate(uint64)).int
debug "validation suite", secondsTillRequestEnd = secondsTillRequestEnd.seconds
discard await waitForRequestToFail(secondsTillRequestEnd + 60)
let secondsTillRequestEnd = await getSecondsTillRequestEnd(requestId)
debug "validation suite", secondsTillRequestEnd = secondsTillRequestEnd
await waitForRequestToFail(requestId, secondsTillRequestEnd.int64)
test "validator uses historical state to mark missing proofs",
NodeConfigs(
@ -124,7 +111,6 @@ marketplacesuite(name = "Validation"):
var currentTime = await ethProvider.currentTime()
let requestEndTime = currentTime.truncate(uint64) + duration
let data = await RandomChunker.example(blocks = blocks)
let datasetSize =
datasetSize(blocks = blocks, nodes = ecNodes, tolerance = ecTolerance)
await createAvailabilities(
@ -134,20 +120,13 @@ marketplacesuite(name = "Validation"):
minPricePerBytePerSecond,
)
let cid = (await client0.upload(data)).get
let purchaseId = await client0.requestStorage(
cid,
expiry = expiry,
duration = duration,
nodes = ecNodes,
tolerance = ecTolerance,
proofProbability = proofProbability,
let (purchaseId, requestId) = await requestStorage(
client0, duration = duration, expiry = expiry, proofProbability = proofProbability
)
let requestId = (await client0.requestId(purchaseId)).get
debug "validation suite", purchaseId = purchaseId.toHex, requestId = requestId
discard await waitForRequestToStart(expiry.int + 60)
await waitForRequestToStart(requestId, expiry.int64)
# extra block just to make sure we have one that separates us
# from the block containing the last (past) SlotFilled event
@ -168,10 +147,6 @@ marketplacesuite(name = "Validation"):
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
discard await waitForRequestToFail(secondsTillRequestEnd + 60)
let secondsTillRequestEnd = await getSecondsTillRequestEnd(requestId)
debug "validation suite", secondsTillRequestEnd = secondsTillRequestEnd
await waitForRequestToFail(requestId, secondsTillRequestEnd.int64)

View File

@ -114,10 +114,11 @@ marketplacesuite(name = "Sales"):
test "updating availability - updating totalSize does not allow bellow utilized",
salesConfig:
let originalSize = 0xFFFFFF.uint64
let data = await RandomChunker.example(blocks = 8)
let minPricePerBytePerSecond = 3.u256
let collateralPerByte = 1.u256
let totalCollateral = originalSize.u256 * collateralPerByte
let expiry = 10 * 60.uint64
let availability = (
await host.postAvailability(
totalSize = originalSize,
@ -128,19 +129,11 @@ marketplacesuite(name = "Sales"):
).get
# Lets create storage request that will utilize some of the availability's space
let cid = (await client.upload(data)).get
let id = await client.requestStorage(
cid,
duration = 20 * 60.uint64,
pricePerBytePerSecond = minPricePerBytePerSecond,
proofProbability = 3.u256,
expiry = (10 * 60).uint64,
collateralPerByte = collateralPerByte,
nodes = 3,
tolerance = 1,
let (purchaseId, requestId) = await requestStorage(
client = client, pricePerBytePerSecond = minPricePerBytePerSecond
)
discard await waitForRequestToStart()
await waitForRequestToStart(requestId, expiry.int64)
let updatedAvailability =
((await host.getAvailabilities()).get).findItem(availability).get
@ -183,12 +176,9 @@ marketplacesuite(name = "Sales"):
test "returns an error when trying to update the until date before an existing a request is finished",
salesConfig:
let size = 0xFFFFFF.uint64
let data = await RandomChunker.example(blocks = 8)
let duration = 20 * 60.uint64
let expiry = 10 * 60.uint64
let minPricePerBytePerSecond = 3.u256
let collateralPerByte = 1.u256
let ecNodes = 3.uint
let ecTolerance = 1.uint
# host makes storage available
let availability = (
@ -200,24 +190,12 @@ marketplacesuite(name = "Sales"):
)
).get
# client requests storage
let cid = (await client.upload(data)).get
let id = (
await client.requestStorage(
cid,
duration = duration,
pricePerBytePerSecond = minPricePerBytePerSecond,
proofProbability = 3.u256,
expiry = 10 * 60.uint64,
collateralPerByte = collateralPerByte,
nodes = ecNodes,
tolerance = ecTolerance,
)
).get
let (purchaseId, requestId) =
await requestStorage(client, pricePerBytePerSecond = minPricePerBytePerSecond)
discard await waitForRequestToStart()
await waitForRequestToStart(requestId, expiry.int64)
let purchase = (await client.getPurchase(id)).get
let purchase = (await client.getPurchase(purchaseId)).get
check purchase.error == none string
let unixNow = getTime().toUnix()

View File

@ -1,5 +1,4 @@
import macros
import std/strutils
import std/unittest
import pkg/chronos
@ -9,7 +8,7 @@ import pkg/codex/contracts/marketplace as mp
import pkg/codex/periods
import pkg/codex/utils/json
from pkg/codex/utils import roundUp, divUp
import ./multinodes except Subscription
import ./multinodes except Subscription, Event
import ../contracts/time
import ../contracts/deployment
@ -22,72 +21,83 @@ template marketplacesuite*(name: string, body: untyped) =
var period: uint64
var periodicity: Periodicity
var token {.inject, used.}: Erc20Token
var requestStartedEvent: AsyncEvent
var requestStartedSubscription: Subscription
var requestFailedEvent: AsyncEvent
var requestFailedSubscription: Subscription
template fail(reason: string) =
raise newException(TestFailedError, reason)
var subscriptions: seq[Subscription] = @[]
var tokenSubscription: Subscription
proc check(cond: bool, reason = "Check failed"): void =
if not cond:
fail(reason)
template stopOnRequestFailed(tbody: untyped) =
let completed = newAsyncEvent()
proc marketplaceSubscribe[E: Event](
event: type E, handler: EventHandler[E]
) {.async.} =
let sub = await marketplace.subscribe(event, handler)
subscriptions.add(sub)
let mainFut = (
proc(): Future[void] {.async.} =
tbody
completed.fire()
)()
proc tokenSubscribe(
handler: proc(event: ?!Transfer) {.gcsafe, raises: [].}
) {.async.} =
let sub = await token.subscribe(Transfer, handler)
tokenSubscription = sub
let fastFailFut = (
proc(): Future[void] {.async.} =
try:
await requestFailedEvent.wait().wait(timeout = chronos.seconds(60))
completed.fire()
raise newException(TestFailedError, "storage request has failed")
except AsyncTimeoutError:
discard
)()
proc subscribeOnRequestFulfilled(
requestId: RequestId
): Future[AsyncEvent] {.async.} =
let event = newAsyncEvent()
await completed.wait().wait(timeout = chronos.seconds(60 * 30))
proc onRequestFulfilled(eventResult: ?!RequestFulfilled) {.raises: [].} =
assert not eventResult.isErr
let er = !eventResult
if not fastFailFut.completed:
await fastFailFut.cancelAndWait()
if er.requestId == requestId:
event.fire()
if mainFut.failed:
raise mainFut.error
let sub = await marketplace.subscribe(RequestFulfilled, onRequestFulfilled)
subscriptions.add(sub)
if fastFailFut.failed:
raise fastFailFut.error
proc onRequestStarted(eventResult: ?!RequestFulfilled) {.raises: [].} =
requestStartedEvent.fire()
proc onRequestFailed(eventResult: ?!RequestFailed) {.raises: [].} =
requestFailedEvent.fire()
return event
proc getCurrentPeriod(): Future[Period] {.async.} =
return periodicity.periodOf((await ethProvider.currentTime()).truncate(uint64))
proc waitForRequestToStart(
seconds = 10 * 60 + 10
): Future[Period] {.
async: (raises: [CancelledError, AsyncTimeoutError, TestFailedError])
.} =
await requestStartedEvent.wait().wait(timeout = chronos.seconds(seconds))
# Recreate a new future if we need to wait for another request
requestStartedEvent = newAsyncEvent()
requestId: RequestId, seconds = 10 * 60 + 10
): Future[void] {.async.} =
let event = newAsyncEvent()
proc onRequestFulfilled(eventResult: ?!RequestFulfilled) {.raises: [].} =
assert not eventResult.isErr
let er = !eventResult
if er.requestId == requestId:
event.fire()
let sub = await marketplace.subscribe(RequestFulfilled, onRequestFulfilled)
subscriptions.add(sub)
await event.wait().wait(timeout = chronos.seconds(seconds))
proc getSecondsTillRequestEnd(requestId: RequestId): Future[int64] {.async.} =
let currentTime = await ethProvider.currentTime()
let requestEnd = await marketplace.requestEnd(requestId)
return requestEnd.int64 - currentTime.truncate(int64)
proc waitForRequestToFail(
seconds = (5 * 60) + 10
): Future[Period] {.async: (raises: [CancelledError, AsyncTimeoutError]).} =
await requestFailedEvent.wait().wait(timeout = chronos.seconds(seconds))
# Recreate a new future if we need to wait for another request
requestFailedEvent = newAsyncEvent()
requestId: RequestId, seconds = (5 * 60) + 10
): Future[void] {.async.} =
let event = newAsyncEvent()
proc onRequestFailed(eventResult: ?!RequestFailed) {.raises: [].} =
assert not eventResult.isErr
let er = !eventResult
if er.requestId == requestId:
event.fire()
let sub = await marketplace.subscribe(RequestFailed, onRequestFailed)
subscriptions.add(sub)
await event.wait().wait(timeout = chronos.seconds(seconds))
proc advanceToNextPeriod() {.async.} =
let periodicity = Periodicity(seconds: period)
@ -145,7 +155,7 @@ template marketplacesuite*(name: string, body: untyped) =
client: CodexClient,
cid: Cid,
proofProbability = 1.u256,
duration: uint64 = 12.periods,
duration: uint64 = 20 * 60.uint64,
pricePerBytePerSecond = 1.u256,
collateralPerByte = 1.u256,
expiry: uint64 = 4.periods,
@ -167,6 +177,36 @@ template marketplacesuite*(name: string, body: untyped) =
return id
proc requestStorage(
client: CodexClient,
proofProbability = 3.u256,
duration = 20 * 60.uint64,
pricePerBytePerSecond = 1.u256,
collateralPerByte = 1.u256,
expiry = 10 * 60.uint64,
nodes = 3,
tolerance = 1,
blocks = 8,
data = seq[byte].none,
): Future[(PurchaseId, RequestId)] {.async.} =
let bytes = data |? await RandomChunker.example(blocks = blocks)
let cid = (await client.upload(bytes)).get
let purchaseId = await client.requestStorage(
cid,
duration = duration,
pricePerBytePerSecond = pricePerBytePerSecond,
proofProbability = proofProbability,
expiry = expiry,
collateralPerByte = collateralPerByte,
nodes = nodes,
tolerance = tolerance,
)
let requestId = (await client.requestId(purchaseId)).get
return (purchaseId, requestId)
setup:
marketplace = Marketplace.new(Marketplace.address, ethProvider.getSigner())
let tokenAddress = await marketplace.token()
@ -174,18 +214,12 @@ template marketplacesuite*(name: string, body: untyped) =
let config = await marketplace.configuration()
period = config.proofs.period
periodicity = Periodicity(seconds: period)
requestStartedEvent = newAsyncEvent()
requestFailedEvent = newAsyncEvent()
requestStartedSubscription =
await marketplace.subscribe(RequestFulfilled, onRequestStarted)
requestFailedSubscription =
await marketplace.subscribe(RequestFailed, onRequestFailed)
subscriptions = @[]
teardown:
await requestStartedSubscription.unsubscribe()
await requestFailedSubscription.unsubscribe()
for subscription in subscriptions:
await subscription.unsubscribe()
if not tokenSubscription.isNil:
await tokenSubscription.unsubscribe()
body

View File

@ -107,7 +107,10 @@ template multinodesuite*(name: string, body: untyped) =
currentTestName = tname
nodeConfigs = startNodeConfigs
test tname:
failAndTeardownOnError("test failed", tbody)
tbody
template fail(reason: string) =
raise newException(TestFailedError, reason)
proc sanitize(pathSegment: string): string =
var sanitized = pathSegment
@ -255,21 +258,34 @@ template multinodesuite*(name: string, body: untyped) =
return await newCodexProcess(validatorIdx, config, Role.Validator)
proc teardownImpl() {.async.} =
proc teardownImpl() {.async: (raises: [CancelledError]).} =
for nodes in @[validators(), clients(), providers()]:
for node in nodes:
await node.stop() # also stops rest client
node.removeDataDir()
try:
await node.stop() # also stops rest client
node.removeDataDir()
except CancelledError as e:
raise e
# Raised by removeDataDir
except Exception as e:
error "error when trying to stop the node", error = e.msg
# if hardhat was started in the test, kill the node
# otherwise revert the snapshot taken in the test setup
let hardhat = hardhat()
if not hardhat.isNil:
await hardhat.stop()
try:
await hardhat.stop()
except CancelledError as e:
raise e
except CatchableError as e:
error "error when trying to stop hardhat", error = e.msg
else:
discard await send(ethProvider, "evm_revert", @[snapshot])
await ethProvider.close()
try:
discard await send(ethProvider, "evm_revert", @[snapshot])
await ethProvider.close()
except ProviderError as e:
error "error when trying to cleanup the evm", error = e.msg
running = @[]
@ -285,7 +301,7 @@ template multinodesuite*(name: string, body: untyped) =
await teardownImpl()
when declared(teardownAllIMPL):
teardownAllIMPL()
raise er
fail(er.msg)
proc updateBootstrapNodes(
node: CodexProcess