Arnaud 7deeb7d2b3
feat(marketplace): persistent availabilities (#1099)
* Add availability enabled parameter

* Return bytes to availability when finished

* Add until parameter

* Remove debug message

* Clean up and fix tests

* Update documentations and cleanup

* Avoid swallowing CancelledError

* Move until validation to reservations module

* Call onAvailabilityAdded callabck when the availability is enabled in sales

* Remove until validation in restapi when creating an availability

* Add openapi documentation

* Use results instead of stew/results (#1112)

* feat: request duration limit (#1057)

* feat: request duration limit

* Fix tests and duration type

* Add custom error

* Remove merge issue

* Update codex contracts eth

* Update market config and fix test

* Fix SlotReservationsConfig syntax

* Update dependencies

* test: remove doubled test

* chore: update contracts repo

---------

Co-authored-by: Arnaud <arnaud@status.im>

* fix(statemachine): do not raise from state.run (#1115)

* fix(statemachine): do not raise from state.run

* fix rebase

* fix exception handling in SaleProvingSimulated.prove

- re-raise CancelledError
- don't return State on CatchableError
- expect the Proofs_InvalidProof custom error instead of checking a string

* asyncSpawn salesagent.onCancelled

This was swallowing a KeyError in one of the tests (fixed in the previous commit)

* remove error handling states in asyncstatemachine

* revert unneeded changes

* formatting

* PR feedback, logging updates

* chore(integration): simplify block expiration integration test (#1100)

* chore(integration): simplify block expiration integration test

* clean up

* fix after rebase

* perf: contract storage optimizations (#1094)

* perf: contract storage optimizations

* Apply optimization changes

* Apply optimizing parameters sizing

* Update codex-contracts-eth

* bump latest changes in contracts branch

* Change requestDurationLimit to uint64

* fix tests

* fix tests

---------

Co-authored-by: Arnaud <arnaud@status.im>
Co-authored-by: Eric <5089238+emizzle@users.noreply.github.com>

* bump contracts to master (#1122)

* Add availability enabled parameter

* Return bytes to availability when finished

* Add until parameter

* Clean up and fix tests

* Move until validation to reservations module

* Apply suggestion changes: return the reservation module error

* Apply suggestion changes for until dates

* Apply suggestion changes: reorganize tests

* Fix indent

* Remove test related to timing issue

* Add raises errors to async pragram and remove useless try except

* Update open api documentation

* Fix wording

* Remove the httpClient restart statements

* Use market.getRequestEnd to set validUntil

* Remove returnBytes

* Use clock.now in testing

* Move the api validation file to the right file

---------

Co-authored-by: Adam Uhlíř <adam@uhlir.dev>
Co-authored-by: Eric <5089238+emizzle@users.noreply.github.com>
2025-03-26 11:45:22 +00:00

237 lines
7.5 KiB
Nim

import std/httpclient
import std/times
import pkg/codex/contracts
from pkg/codex/stores/repostore/types import DefaultQuotaBytes
import ./twonodes
import ../codex/examples
import ../contracts/time
import ./codexconfig
import ./codexclient
import ./nodeconfigs
proc findItem[T](items: seq[T], item: T): ?!T =
for tmp in items:
if tmp == item:
return success tmp
return failure("Not found")
multinodesuite "Sales":
let salesConfig = NodeConfigs(
clients: CodexConfigs.init(nodes = 1).some,
providers: CodexConfigs.init(nodes = 1)
# .debug() # uncomment to enable console log output
# .withLogFile() # uncomment to output log file to tests/integration/logs/<start_datetime> <suite_name>/<test_name>/<node_role>_<node_idx>.log
# .withLogTopics("node", "marketplace", "sales", "reservations", "node", "proving", "clock")
.some,
)
var host: CodexClient
var client: CodexClient
setup:
host = providers()[0].client
client = clients()[0].client
test "node handles new storage availability", salesConfig:
let availability1 = (
await host.postAvailability(
totalSize = 1.uint64,
duration = 2.uint64,
minPricePerBytePerSecond = 3.u256,
totalCollateral = 4.u256,
)
).get
let availability2 = (
await host.postAvailability(
totalSize = 4.uint64,
duration = 5.uint64,
minPricePerBytePerSecond = 6.u256,
totalCollateral = 7.u256,
)
).get
check availability1 != availability2
test "node lists storage that is for sale", salesConfig:
let availability = (
await host.postAvailability(
totalSize = 1.uint64,
duration = 2.uint64,
minPricePerBytePerSecond = 3.u256,
totalCollateral = 4.u256,
)
).get
check availability in (await host.getAvailabilities()).get
test "updating availability", salesConfig:
let availability = (
await host.postAvailability(
totalSize = 140000.uint64,
duration = 200.uint64,
minPricePerBytePerSecond = 3.u256,
totalCollateral = 300.u256,
)
).get
var until = getTime().toUnix()
await host.patchAvailability(
availability.id,
duration = 100.uint64.some,
minPricePerBytePerSecond = 2.u256.some,
totalCollateral = 200.u256.some,
enabled = false.some,
until = until.some,
)
let updatedAvailability =
((await host.getAvailabilities()).get).findItem(availability).get
check updatedAvailability.duration == 100.uint64
check updatedAvailability.minPricePerBytePerSecond == 2
check updatedAvailability.totalCollateral == 200
check updatedAvailability.totalSize == 140000.uint64
check updatedAvailability.freeSize == 140000.uint64
check updatedAvailability.enabled == false
check updatedAvailability.until == until
test "updating availability - updating totalSize", salesConfig:
let availability = (
await host.postAvailability(
totalSize = 140000.uint64,
duration = 200.uint64,
minPricePerBytePerSecond = 3.u256,
totalCollateral = 300.u256,
)
).get
await host.patchAvailability(availability.id, totalSize = 100000.uint64.some)
let updatedAvailability =
((await host.getAvailabilities()).get).findItem(availability).get
check updatedAvailability.totalSize == 100000
check updatedAvailability.freeSize == 100000
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 availability = (
await host.postAvailability(
totalSize = originalSize,
duration = 20 * 60.uint64,
minPricePerBytePerSecond = minPricePerBytePerSecond,
totalCollateral = totalCollateral,
)
).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,
)
).get
check eventually(
await client.purchaseStateIs(id, "started"), timeout = 10 * 60 * 1000
)
let updatedAvailability =
((await host.getAvailabilities()).get).findItem(availability).get
check updatedAvailability.totalSize != updatedAvailability.freeSize
let utilizedSize = updatedAvailability.totalSize - updatedAvailability.freeSize
let totalSizeResponse = (
await host.patchAvailabilityRaw(
availability.id, totalSize = (utilizedSize - 1).some
)
)
check totalSizeResponse.status == 422
check "totalSize must be larger then current totalSize" in
(await totalSizeResponse.body)
await host.patchAvailability(
availability.id, totalSize = (originalSize + 20000).some
)
let newUpdatedAvailability =
((await host.getAvailabilities()).get).findItem(availability).get
check newUpdatedAvailability.totalSize == originalSize + 20000
check newUpdatedAvailability.freeSize - updatedAvailability.freeSize == 20000
test "updating availability fails with until negative", salesConfig:
let availability = (
await host.postAvailability(
totalSize = 140000.uint64,
duration = 200.uint64,
minPricePerBytePerSecond = 3.u256,
totalCollateral = 300.u256,
)
).get
let response =
await host.patchAvailabilityRaw(availability.id, until = -1.SecondsSince1970.some)
check:
(await response.body) == "Cannot set until to a negative value"
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 minPricePerBytePerSecond = 3.u256
let collateralPerByte = 1.u256
let ecNodes = 3.uint
let ecTolerance = 1.uint
# host makes storage available
let availability = (
await host.postAvailability(
totalSize = size,
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 = duration,
pricePerBytePerSecond = minPricePerBytePerSecond,
proofProbability = 3.u256,
expiry = 10 * 60.uint64,
collateralPerByte = collateralPerByte,
nodes = ecNodes,
tolerance = ecTolerance,
)
).get
check eventually(
await client.purchaseStateIs(id, "started"), timeout = 10 * 60 * 1000
)
let purchase = (await client.getPurchase(id)).get
check purchase.error == none string
let unixNow = getTime().toUnix()
let until = unixNow + 1.SecondsSince1970
let response = await host.patchAvailabilityRaw(
availabilityId = availability.id, until = until.some
)
check:
response.status == 422
(await response.body) ==
"Until parameter must be greater or equal to the longest currently hosted slot"