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

View File

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

View File

@ -271,7 +271,9 @@ proc load*(sales: Sales) {.async.} =
agent.start(SaleUnknown()) agent.start(SaleUnknown())
sales.agents.add agent 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 ## 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 ## it was paused and any slots in the queue should have their `seen` flag
## cleared. ## cleared.
@ -483,7 +485,7 @@ proc startSlotQueue(sales: Sales) =
slotQueue.start() slotQueue.start()
proc onAvailabilityAdded(availability: Availability) {.async.} = proc onAvailabilityAdded(availability: Availability) {.async: (raises: []).} =
await sales.onAvailabilityAdded(availability) await sales.onAvailabilityAdded(availability)
reservations.onAvailabilityAdded = onAvailabilityAdded reservations.onAvailabilityAdded = onAvailabilityAdded

View File

@ -70,6 +70,10 @@ type
minPricePerBytePerSecond* {.serialize.}: UInt256 minPricePerBytePerSecond* {.serialize.}: UInt256
totalCollateral {.serialize.}: UInt256 totalCollateral {.serialize.}: UInt256
totalRemainingCollateral* {.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 Reservation* = ref object
id* {.serialize.}: ReservationId id* {.serialize.}: ReservationId
@ -86,8 +90,9 @@ type
GetNext* = proc(): Future[?seq[byte]] {.upraises: [], gcsafe, closure.} GetNext* = proc(): Future[?seq[byte]] {.upraises: [], gcsafe, closure.}
IterDispose* = proc(): Future[?!void] {.gcsafe, closure.} IterDispose* = proc(): Future[?!void] {.gcsafe, closure.}
OnAvailabilityAdded* = OnAvailabilityAdded* = proc(availability: Availability): Future[void] {.
proc(availability: Availability): Future[void] {.upraises: [], gcsafe.} upraises: [], gcsafe, async: (raises: [])
.}
StorableIter* = ref object StorableIter* = ref object
finished*: bool finished*: bool
next*: GetNext next*: GetNext
@ -128,6 +133,7 @@ proc init*(
duration: UInt256, duration: UInt256,
minPricePerBytePerSecond: UInt256, minPricePerBytePerSecond: UInt256,
totalCollateral: UInt256, totalCollateral: UInt256,
enabled: bool,
): Availability = ): Availability =
var id: array[32, byte] var id: array[32, byte]
doAssert randomBytes(id) == 32 doAssert randomBytes(id) == 32
@ -139,6 +145,7 @@ proc init*(
minPricePerBytePerSecond: minPricePerBytePerSecond, minPricePerBytePerSecond: minPricePerBytePerSecond,
totalCollateral: totalCollateral, totalCollateral: totalCollateral,
totalRemainingCollateral: totalCollateral, totalRemainingCollateral: totalCollateral,
enabled: enabled,
) )
func totalCollateral*(self: Availability): UInt256 {.inline.} = func totalCollateral*(self: Availability): UInt256 {.inline.} =
@ -268,18 +275,8 @@ proc updateAvailability(
trace "Creating new Availability" trace "Creating new Availability"
let res = await self.updateImpl(obj) let res = await self.updateImpl(obj)
# inform subscribers that Availability has been added # inform subscribers that Availability has been added
if onAvailabilityAdded =? self.onAvailabilityAdded: if obj.enabled and 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) 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 return res
else: else:
return failure(err) return failure(err)
@ -304,22 +301,11 @@ proc updateAvailability(
let res = await self.updateImpl(obj) 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 # inform subscribers that Availability has been modified (with increased
# size) # size)
if onAvailabilityAdded =? self.onAvailabilityAdded: 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) 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 return res
proc update*(self: Reservations, obj: Reservation): Future[?!void] {.async.} = proc update*(self: Reservations, obj: Reservation): Future[?!void] {.async.} =
@ -393,12 +379,14 @@ proc createAvailability*(
duration: UInt256, duration: UInt256,
minPricePerBytePerSecond: UInt256, minPricePerBytePerSecond: UInt256,
totalCollateral: UInt256, totalCollateral: UInt256,
enabled: bool,
): Future[?!Availability] {.async.} = ): Future[?!Availability] {.async.} =
trace "creating availability", trace "creating availability",
size, duration, minPricePerBytePerSecond, totalCollateral size, duration, minPricePerBytePerSecond, totalCollateral, enabled
let availability = let availability = Availability.init(
Availability.init(size, size, duration, minPricePerBytePerSecond, totalCollateral) size, size, duration, minPricePerBytePerSecond, totalCollateral, enabled
)
let bytes = availability.freeSize.truncate(uint) let bytes = availability.freeSize.truncate(uint)
if reserveErr =? (await self.repo.reserve(bytes.NBytes)).errorOption: if reserveErr =? (await self.repo.reserve(bytes.NBytes)).errorOption:
@ -651,11 +639,13 @@ proc findAvailability*(
for item in storables.items: for item in storables.items:
if bytes =? (await item) and availability =? Availability.fromJson(bytes): 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 collateralPerByte <= availability.maxCollateralPerByte and
pricePerBytePerSecond >= availability.minPricePerBytePerSecond: pricePerBytePerSecond >= availability.minPricePerBytePerSecond:
trace "availability matched", trace "availability matched",
id = availability.id, id = availability.id,
enabled = availability.enabled,
size, size,
availFreeSize = availability.freeSize, availFreeSize = availability.freeSize,
duration, duration,
@ -675,6 +665,7 @@ proc findAvailability*(
trace "availability did not match", trace "availability did not match",
id = availability.id, id = availability.id,
enabled = availability.enabled,
size, size,
availFreeSize = availability.freeSize, availFreeSize = availability.freeSize,
duration, duration,

View File

@ -163,6 +163,10 @@ components:
totalCollateral: totalCollateral:
type: string type: string
description: Total collateral (in amount of tokens) that can be used for matching requests 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: SalesAvailabilityREAD:
allOf: allOf:

View File

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

View File

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

View File

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

View File

@ -149,6 +149,7 @@ asyncchecksuite "Sales":
duration = 60.u256, duration = 60.u256,
minPricePerBytePerSecond = minPricePerBytePerSecond, minPricePerBytePerSecond = minPricePerBytePerSecond,
totalCollateral = totalCollateral, totalCollateral = totalCollateral,
enabled = true,
) )
request = StorageRequest( request = StorageRequest(
ask: StorageAsk( ask: StorageAsk(
@ -216,10 +217,10 @@ asyncchecksuite "Sales":
let key = availability.id.key.get let key = availability.id.key.get
(waitFor reservations.get(key, Availability)).get (waitFor reservations.get(key, Availability)).get
proc createAvailability() = proc createAvailability(enabled = true) =
let a = waitFor reservations.createAvailability( let a = waitFor reservations.createAvailability(
availability.totalSize, availability.duration, availability.totalSize, availability.duration,
availability.minPricePerBytePerSecond, availability.totalCollateral, availability.minPricePerBytePerSecond, availability.totalCollateral, enabled,
) )
availability = a.get # update id availability = a.get # update id
@ -412,6 +413,20 @@ asyncchecksuite "Sales":
market.slotState[request.slotId(3.u256)] = SlotState.Filled market.slotState[request.slotId(3.u256)] = SlotState.Filled
check wasIgnored() 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": test "retrieves and stores data locally":
var storingRequest: StorageRequest var storingRequest: StorageRequest
var storingSlot: UInt256 var storingSlot: UInt256

View File

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

View File

@ -29,6 +29,7 @@ twonodessuite "REST API":
duration = 2.u256, duration = 2.u256,
minPricePerBytePerSecond = minPricePerBytePerSecond, minPricePerBytePerSecond = minPricePerBytePerSecond,
totalCollateral = totalCollateral, totalCollateral = totalCollateral,
enabled = true.some,
).get ).get
let space = client1.space().tryGet() let space = client1.space().tryGet()
check: check:
@ -37,6 +38,19 @@ twonodessuite "REST API":
space.quotaUsedBytes == 65598.NBytes space.quotaUsedBytes == 65598.NBytes
space.quotaReservedBytes == 12.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: test "node lists local files", twoNodesConfig:
let content1 = "some file contents" let content1 = "some file contents"
let content2 = "some other contents" let content2 = "some other contents"

View File

@ -108,6 +108,18 @@ multinodesuite "Sales":
check updatedAvailability.totalSize == 100000 check updatedAvailability.totalSize == 100000
check updatedAvailability.freeSize == 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", test "updating availability - updating totalSize does not allow bellow utilized",
salesConfig: salesConfig:
let originalSize = 0xFFFFFF.u256 let originalSize = 0xFFFFFF.u256