Add availability enabled parameter

This commit is contained in:
Arnaud 2025-01-29 17:20:02 +01:00
parent 58a962add8
commit b80b1f85be
No known key found for this signature in database
GPG Key ID: 69D6CE281FCAE663
13 changed files with 136 additions and 46 deletions

View File

@ -439,8 +439,11 @@ proc initSalesApi(node: CodexNodeRef, router: var RestRouter) =
without availability =? (
await reservations.createAvailability(
restAv.totalSize, restAv.duration, restAv.minPricePerBytePerSecond,
restAv.totalSize,
restAv.duration,
restAv.minPricePerBytePerSecond,
restAv.totalCollateral,
enabled = restAv.enabled |? true,
)
), error:
return RestApiResponse.error(Http500, error.msg, headers = headers)
@ -526,6 +529,8 @@ proc initSalesApi(node: CodexNodeRef, router: var RestRouter) =
if totalCollateral =? restAv.totalCollateral:
availability.totalCollateral = totalCollateral
availability.enabled = restAv.enabled |? true
if err =? (await reservations.update(availability)).errorOption:
return RestApiResponse.error(Http500, err.msg)

View File

@ -33,6 +33,7 @@ type
minPricePerBytePerSecond* {.serialize.}: UInt256
totalCollateral* {.serialize.}: UInt256
freeSize* {.serialize.}: ?UInt256
enabled* {.serialize.}: ?bool
RestSalesAgent* = object
state* {.serialize.}: string

View File

@ -271,7 +271,9 @@ proc load*(sales: Sales) {.async.} =
agent.start(SaleUnknown())
sales.agents.add agent
proc onAvailabilityAdded(sales: Sales, availability: Availability) {.async.} =
proc onAvailabilityAdded(
sales: Sales, availability: Availability
) {.async: (raises: []).} =
## When availabilities are modified or added, the queue should be unpaused if
## it was paused and any slots in the queue should have their `seen` flag
## cleared.
@ -483,7 +485,7 @@ proc startSlotQueue(sales: Sales) =
slotQueue.start()
proc onAvailabilityAdded(availability: Availability) {.async.} =
proc onAvailabilityAdded(availability: Availability) {.async: (raises: []).} =
await sales.onAvailabilityAdded(availability)
reservations.onAvailabilityAdded = onAvailabilityAdded

View File

@ -70,6 +70,10 @@ type
minPricePerBytePerSecond* {.serialize.}: UInt256
totalCollateral {.serialize.}: UInt256
totalRemainingCollateral* {.serialize.}: UInt256
# If false, the availability will not be able to receive new slots.
# If it is turned on and the availability is already hosting slots,
# it will not affect those existing slots.
enabled* {.serialize.}: bool
Reservation* = ref object
id* {.serialize.}: ReservationId
@ -86,8 +90,9 @@ type
GetNext* = proc(): Future[?seq[byte]] {.upraises: [], gcsafe, closure.}
IterDispose* = proc(): Future[?!void] {.gcsafe, closure.}
OnAvailabilityAdded* =
proc(availability: Availability): Future[void] {.upraises: [], gcsafe.}
OnAvailabilityAdded* = proc(availability: Availability): Future[void] {.
upraises: [], gcsafe, async: (raises: [])
.}
StorableIter* = ref object
finished*: bool
next*: GetNext
@ -128,6 +133,7 @@ proc init*(
duration: UInt256,
minPricePerBytePerSecond: UInt256,
totalCollateral: UInt256,
enabled: bool,
): Availability =
var id: array[32, byte]
doAssert randomBytes(id) == 32
@ -139,6 +145,7 @@ proc init*(
minPricePerBytePerSecond: minPricePerBytePerSecond,
totalCollateral: totalCollateral,
totalRemainingCollateral: totalCollateral,
enabled: enabled,
)
func totalCollateral*(self: Availability): UInt256 {.inline.} =
@ -268,18 +275,8 @@ proc updateAvailability(
trace "Creating new Availability"
let res = await self.updateImpl(obj)
# inform subscribers that Availability has been added
if onAvailabilityAdded =? self.onAvailabilityAdded:
# when chronos v4 is implemented, and OnAvailabilityAdded is annotated
# with async:(raises:[]), we can remove this try/catch as we know, with
# certainty, that nothing will be raised
try:
if obj.enabled and onAvailabilityAdded =? self.onAvailabilityAdded:
await onAvailabilityAdded(obj)
except CancelledError as e:
raise e
except CatchableError as e:
# we don't have any insight into types of exceptions that
# `onAvailabilityAdded` can raise because it is caller-defined
warn "Unknown error during 'onAvailabilityAdded' callback", error = e.msg
return res
else:
return failure(err)
@ -304,22 +301,11 @@ proc updateAvailability(
let res = await self.updateImpl(obj)
if oldAvailability.freeSize < obj.freeSize: # availability added
if obj.enabled and oldAvailability.freeSize < obj.freeSize: # availability added
# inform subscribers that Availability has been modified (with increased
# size)
if onAvailabilityAdded =? self.onAvailabilityAdded:
# when chronos v4 is implemented, and OnAvailabilityAdded is annotated
# with async:(raises:[]), we can remove this try/catch as we know, with
# certainty, that nothing will be raised
try:
await onAvailabilityAdded(obj)
except CancelledError as e:
raise e
except CatchableError as e:
# we don't have any insight into types of exceptions that
# `onAvailabilityAdded` can raise because it is caller-defined
warn "Unknown error during 'onAvailabilityAdded' callback", error = e.msg
return res
proc update*(self: Reservations, obj: Reservation): Future[?!void] {.async.} =
@ -393,12 +379,14 @@ proc createAvailability*(
duration: UInt256,
minPricePerBytePerSecond: UInt256,
totalCollateral: UInt256,
enabled: bool,
): Future[?!Availability] {.async.} =
trace "creating availability",
size, duration, minPricePerBytePerSecond, totalCollateral
size, duration, minPricePerBytePerSecond, totalCollateral, enabled
let availability =
Availability.init(size, size, duration, minPricePerBytePerSecond, totalCollateral)
let availability = Availability.init(
size, size, duration, minPricePerBytePerSecond, totalCollateral, enabled
)
let bytes = availability.freeSize.truncate(uint)
if reserveErr =? (await self.repo.reserve(bytes.NBytes)).errorOption:
@ -651,11 +639,13 @@ proc findAvailability*(
for item in storables.items:
if bytes =? (await item) and availability =? Availability.fromJson(bytes):
if size <= availability.freeSize and duration <= availability.duration and
if availability.enabled and size <= availability.freeSize and
duration <= availability.duration and
collateralPerByte <= availability.maxCollateralPerByte and
pricePerBytePerSecond >= availability.minPricePerBytePerSecond:
trace "availability matched",
id = availability.id,
enabled = availability.enabled,
size,
availFreeSize = availability.freeSize,
duration,
@ -675,6 +665,7 @@ proc findAvailability*(
trace "availability did not match",
id = availability.id,
enabled = availability.enabled,
size,
availFreeSize = availability.freeSize,
duration,

View File

@ -163,6 +163,10 @@ components:
totalCollateral:
type: string
description: Total collateral (in amount of tokens) that can be used for matching requests
enabled:
type: boolean
description: Enable the ability to receive sales on this availability.
default: true
SalesAvailabilityREAD:
allOf:

View File

@ -65,6 +65,7 @@ proc example*(
duration = uint16.example.u256,
minPricePerBytePerSecond = uint8.example.u256,
totalCollateral = totalSize * collateralPerByte,
enabled = true,
)
proc example*(_: type Reservation): Reservation =

View File

@ -39,6 +39,7 @@ asyncchecksuite "sales state 'preparing'":
duration = request.ask.duration + 60.u256,
minPricePerBytePerSecond = request.ask.pricePerBytePerSecond,
totalCollateral = request.ask.collateralPerSlot * request.ask.slots.u256,
enabled = true,
)
let repoDs = SQLiteDatastore.new(Memory).tryGet()
let metaDs = SQLiteDatastore.new(Memory).tryGet()
@ -67,10 +68,10 @@ asyncchecksuite "sales state 'preparing'":
let next = state.onSlotFilled(request.id, slotIndex)
check !next of SaleFilled
proc createAvailability() {.async.} =
proc createAvailability(enabled = true) {.async.} =
let a = await reservations.createAvailability(
availability.totalSize, availability.duration,
availability.minPricePerBytePerSecond, availability.totalCollateral,
availability.minPricePerBytePerSecond, availability.totalCollateral, enabled,
)
availability = a.get
@ -81,6 +82,11 @@ asyncchecksuite "sales state 'preparing'":
check ignored.reprocessSlot
check ignored.returnBytes == false
test "run switches to ignored when a availability is not enabled":
await createAvailability(enabled = false)
let next = !(await state.run(agent))
check next of SaleIgnored
test "run switches to slot reserving state after reservation created":
await createAvailability()
let next = await state.run(agent)

View File

@ -39,12 +39,13 @@ asyncchecksuite "Reservations module":
await repoTmp.destroyDb()
await metaTmp.destroyDb()
proc createAvailability(): Availability =
proc createAvailability(enabled = true): Availability =
let example = Availability.example(collateralPerByte)
let totalSize = rand(100000 .. 200000).u256
let totalCollateral = totalSize * collateralPerByte
let availability = waitFor reservations.createAvailability(
totalSize, example.duration, example.minPricePerBytePerSecond, totalCollateral
totalSize, example.duration, example.minPricePerBytePerSecond, totalCollateral,
enabled,
)
return availability.get
@ -64,8 +65,8 @@ asyncchecksuite "Reservations module":
check (await reservations.all(Availability)).get.len == 0
test "generates unique ids for storage availability":
let availability1 = Availability.init(1.u256, 2.u256, 3.u256, 4.u256, 5.u256)
let availability2 = Availability.init(1.u256, 2.u256, 3.u256, 4.u256, 5.u256)
let availability1 = Availability.init(1.u256, 2.u256, 3.u256, 4.u256, 5.u256, true)
let availability2 = Availability.init(1.u256, 2.u256, 3.u256, 4.u256, 5.u256, true)
check availability1.id != availability2.id
test "can reserve available storage":
@ -285,7 +286,9 @@ asyncchecksuite "Reservations module":
test "onAvailabilityAdded called when availability is created":
var added: Availability
reservations.onAvailabilityAdded = proc(a: Availability) {.async.} =
reservations.onAvailabilityAdded = proc(
a: Availability
) {.gcsafe, async: (raises: []).} =
added = a
let availability = createAvailability()
@ -295,7 +298,9 @@ asyncchecksuite "Reservations module":
test "onAvailabilityAdded called when availability size is increased":
var availability = createAvailability()
var added: Availability
reservations.onAvailabilityAdded = proc(a: Availability) {.async.} =
reservations.onAvailabilityAdded = proc(
a: Availability
) {.gcsafe, async: (raises: []).} =
added = a
availability.freeSize += 1.u256
discard await reservations.update(availability)
@ -305,7 +310,21 @@ asyncchecksuite "Reservations module":
test "onAvailabilityAdded is not called when availability size is decreased":
var availability = createAvailability()
var called = false
reservations.onAvailabilityAdded = proc(a: Availability) {.async.} =
reservations.onAvailabilityAdded = proc(
a: Availability
) {.gcsafe, async: (raises: []).} =
called = true
availability.freeSize -= 1.u256
discard await reservations.update(availability)
check not called
test "onAvailabilityAdded is not called when enabled is false":
var availability = createAvailability(enabled = false)
var called = false
reservations.onAvailabilityAdded = proc(
a: Availability
) {.gcsafe, async: (raises: []).} =
called = true
availability.freeSize -= 1.u256
discard await reservations.update(availability)
@ -323,6 +342,16 @@ asyncchecksuite "Reservations module":
check found.isSome
check found.get == availability
test "availabilities cannot be found when it is not enabled":
let availability = createAvailability(enabled = false)
let found = await reservations.findAvailability(
availability.freeSize, availability.duration,
availability.minPricePerBytePerSecond, collateralPerByte,
)
check found.isNone
test "non-matching availabilities are not found":
let availability = createAvailability()
@ -364,6 +393,7 @@ asyncchecksuite "Reservations module":
UInt256.example,
UInt256.example,
UInt256.example,
enabled = true,
)
check created.isErr
check created.error of ReserveFailedError

View File

@ -149,6 +149,7 @@ asyncchecksuite "Sales":
duration = 60.u256,
minPricePerBytePerSecond = minPricePerBytePerSecond,
totalCollateral = totalCollateral,
enabled = true,
)
request = StorageRequest(
ask: StorageAsk(
@ -216,10 +217,10 @@ asyncchecksuite "Sales":
let key = availability.id.key.get
(waitFor reservations.get(key, Availability)).get
proc createAvailability() =
proc createAvailability(enabled = true) =
let a = waitFor reservations.createAvailability(
availability.totalSize, availability.duration,
availability.minPricePerBytePerSecond, availability.totalCollateral,
availability.minPricePerBytePerSecond, availability.totalCollateral, enabled,
)
availability = a.get # update id
@ -412,6 +413,20 @@ asyncchecksuite "Sales":
market.slotState[request.slotId(3.u256)] = SlotState.Filled
check wasIgnored()
test "ignores request when availability is not enabled":
createAvailability(enabled = false)
await market.requestStorage(request)
check wasIgnored()
test "ignores request when availability was disabled after the request storage is created":
createAvailability(enabled = true)
await market.requestStorage(request)
availability.enabled = false
discard await reservations.update(availability)
check wasIgnored()
test "retrieves and stores data locally":
var storingRequest: StorageRequest
var storingSlot: UInt256

View File

@ -175,6 +175,7 @@ proc getSlots*(client: CodexClient): ?!seq[Slot] =
proc postAvailability*(
client: CodexClient,
totalSize, duration, minPricePerBytePerSecond, totalCollateral: UInt256,
enabled: ?bool = bool.none,
): ?!Availability =
## Post sales availability endpoint
##
@ -185,6 +186,7 @@ proc postAvailability*(
"duration": duration,
"minPricePerBytePerSecond": minPricePerBytePerSecond,
"totalCollateral": totalCollateral,
"enabled": enabled |? true,
}
let response = client.http.post(url, $json)
doAssert response.status == "201 Created",
@ -196,6 +198,7 @@ proc patchAvailabilityRaw*(
availabilityId: AvailabilityId,
totalSize, freeSize, duration, minPricePerBytePerSecond, totalCollateral: ?UInt256 =
UInt256.none,
enabled: ?bool = bool.none,
): Response =
## Updates availability
##
@ -219,6 +222,9 @@ proc patchAvailabilityRaw*(
if totalCollateral =? totalCollateral:
json["totalCollateral"] = %totalCollateral
if enabled =? enabled:
json["enabled"] = %enabled
client.http.patch(url, $json)
proc patchAvailability*(
@ -226,6 +232,7 @@ proc patchAvailability*(
availabilityId: AvailabilityId,
totalSize, duration, minPricePerBytePerSecond, totalCollateral: ?UInt256 =
UInt256.none,
enabled: ?bool = bool.none,
): void =
let response = client.patchAvailabilityRaw(
availabilityId,
@ -233,6 +240,7 @@ proc patchAvailability*(
duration = duration,
minPricePerBytePerSecond = minPricePerBytePerSecond,
totalCollateral = totalCollateral,
enabled = enabled,
)
doAssert response.status == "200 OK", "expected 200 OK, got " & response.status

View File

@ -266,7 +266,8 @@ marketplacesuite "Simulate invalid proofs":
# totalSize=slotSize, # should match 1 slot only
# duration=totalPeriods.periods.u256,
# minPricePerBytePerSecond=minPricePerBytePerSecond,
# totalCollateral=slotSize * minPricePerBytePerSecond
# totalCollateral=slotSize * minPricePerBytePerSecond,
# enabled = true.some
# )
# let cid = client0.upload(data).get

View File

@ -29,6 +29,7 @@ twonodessuite "REST API":
duration = 2.u256,
minPricePerBytePerSecond = minPricePerBytePerSecond,
totalCollateral = totalCollateral,
enabled = true.some,
).get
let space = client1.space().tryGet()
check:
@ -37,6 +38,19 @@ twonodessuite "REST API":
space.quotaUsedBytes == 65598.NBytes
space.quotaReservedBytes == 12.NBytes
test "created correctly an availability when not enabled by default", twoNodesConfig:
let totalSize = 12.u256
let minPricePerBytePerSecond = 1.u256
let totalCollateral = totalSize * minPricePerBytePerSecond
let availability = client1.postAvailability(
totalSize = totalSize,
duration = 2.u256,
minPricePerBytePerSecond = minPricePerBytePerSecond,
totalCollateral = totalCollateral,
enabled = false.some,
).get
check availability.enabled == false
test "node lists local files", twoNodesConfig:
let content1 = "some file contents"
let content2 = "some other contents"

View File

@ -108,6 +108,18 @@ multinodesuite "Sales":
check updatedAvailability.totalSize == 100000
check updatedAvailability.freeSize == 100000
test "updating availability - updating enabled", salesConfig:
let availability = host.postAvailability(
totalSize = 140000.u256,
duration = 200.u256,
minPricePerBytePerSecond = 3.u256,
totalCollateral = 300.u256,
enabled = true.some,
).get
host.patchAvailability(availability.id, enabled = false.some)
let updatedAvailability = (host.getAvailabilities().get).findItem(availability).get
check updatedAvailability.enabled == false
test "updating availability - updating totalSize does not allow bellow utilized",
salesConfig:
let originalSize = 0xFFFFFF.u256