chore: wip
This commit is contained in:
parent
de1714ed06
commit
8a0f6f119b
|
@ -46,6 +46,27 @@ logScope:
|
||||||
declareCounter(codex_api_uploads, "codex API uploads")
|
declareCounter(codex_api_uploads, "codex API uploads")
|
||||||
declareCounter(codex_api_downloads, "codex API downloads")
|
declareCounter(codex_api_downloads, "codex API downloads")
|
||||||
|
|
||||||
|
proc getLongestRequestEnd(node: CodexNodeRef, availabilityId: AvailabilityId): ?!SecondsSince1970 =
|
||||||
|
without contracts =? node.contracts.host:
|
||||||
|
return failure("Sales unavailable")
|
||||||
|
|
||||||
|
let
|
||||||
|
reservations = contracts.sales.context.reservations
|
||||||
|
market = contracts.sales.context.market
|
||||||
|
requestEndFutures = reservations.all(Reservation, availabilityId).mapIt(market.getRequestEnd(it.requestId))
|
||||||
|
|
||||||
|
if len(requestEndFutures) == 0:
|
||||||
|
return success(0)
|
||||||
|
|
||||||
|
try:
|
||||||
|
let requestEnds = await allFutures(requestEndFutures)
|
||||||
|
|
||||||
|
return success(requestEnds.reduce(max))
|
||||||
|
except CancelledError as exc:
|
||||||
|
raise exc
|
||||||
|
except CatchableError as exc:
|
||||||
|
return failure(exc.msg)
|
||||||
|
|
||||||
proc validate(
|
proc validate(
|
||||||
pattern: string,
|
pattern: string,
|
||||||
value: string): int
|
value: string): int
|
||||||
|
@ -276,6 +297,9 @@ proc initSalesApi(node: CodexNodeRef, router: var RestRouter) =
|
||||||
if restAv.totalSize == 0:
|
if restAv.totalSize == 0:
|
||||||
return RestApiResponse.error(Http400, "Total size must be larger then zero")
|
return RestApiResponse.error(Http400, "Total size must be larger then zero")
|
||||||
|
|
||||||
|
if restAv.until < 0:
|
||||||
|
return RestApiResponse.error(Http400, "Until parameter has to be positive integer")
|
||||||
|
|
||||||
if not reservations.hasAvailable(restAv.totalSize.truncate(uint)):
|
if not reservations.hasAvailable(restAv.totalSize.truncate(uint)):
|
||||||
return RestApiResponse.error(Http422, "Not enough storage quota")
|
return RestApiResponse.error(Http422, "Not enough storage quota")
|
||||||
|
|
||||||
|
@ -284,7 +308,9 @@ proc initSalesApi(node: CodexNodeRef, router: var RestRouter) =
|
||||||
restAv.totalSize,
|
restAv.totalSize,
|
||||||
restAv.duration,
|
restAv.duration,
|
||||||
restAv.minPrice,
|
restAv.minPrice,
|
||||||
restAv.maxCollateral)
|
restAv.maxCollateral,
|
||||||
|
restAv.until,
|
||||||
|
restAv.enabled |? true)
|
||||||
), error:
|
), error:
|
||||||
return RestApiResponse.error(Http500, error.msg)
|
return RestApiResponse.error(Http500, error.msg)
|
||||||
|
|
||||||
|
@ -350,6 +376,19 @@ proc initSalesApi(node: CodexNodeRef, router: var RestRouter) =
|
||||||
if maxCollateral =? restAv.maxCollateral:
|
if maxCollateral =? restAv.maxCollateral:
|
||||||
availability.maxCollateral = maxCollateral
|
availability.maxCollateral = maxCollateral
|
||||||
|
|
||||||
|
if enabled =? restAv.enabled:
|
||||||
|
availability.enabled = enabled
|
||||||
|
|
||||||
|
if until =? restAv.until:
|
||||||
|
if until < 0:
|
||||||
|
return RestApiResponse.error(Http400, "Until parameter must be greater or equal 0. Got: " & $until)
|
||||||
|
|
||||||
|
let longestRequestEnd = node.getLongestRequestEnd(id)
|
||||||
|
if until != 0 && until < longestRequestEnd:
|
||||||
|
return RestApiResponse.error(Http400, "Until parameter must be greater or equal the current longest request. Longest request ends at: " & $longestRequestEnd)
|
||||||
|
|
||||||
|
availability.until = until
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
|
|
|
@ -28,10 +28,12 @@ type
|
||||||
|
|
||||||
RestAvailability* = object
|
RestAvailability* = object
|
||||||
totalSize* {.serialize.}: UInt256
|
totalSize* {.serialize.}: UInt256
|
||||||
|
freeSize* {.serialize.}: ?UInt256
|
||||||
duration* {.serialize.}: UInt256
|
duration* {.serialize.}: UInt256
|
||||||
minPrice* {.serialize.}: UInt256
|
minPrice* {.serialize.}: UInt256
|
||||||
maxCollateral* {.serialize.}: UInt256
|
maxCollateral* {.serialize.}: UInt256
|
||||||
freeSize* {.serialize.}: ?UInt256
|
until* {.serialize.}: int64
|
||||||
|
enabled* {.serialize.}: ?bool
|
||||||
|
|
||||||
RestSalesAgent* = object
|
RestSalesAgent* = object
|
||||||
state* {.serialize.}: string
|
state* {.serialize.}: string
|
||||||
|
|
|
@ -90,7 +90,7 @@ func new*(_: type Sales,
|
||||||
repo: RepoStore,
|
repo: RepoStore,
|
||||||
simulateProofFailures: int): Sales =
|
simulateProofFailures: int): Sales =
|
||||||
|
|
||||||
let reservations = Reservations.new(repo)
|
let reservations = Reservations.new(repo, clock)
|
||||||
Sales(
|
Sales(
|
||||||
context: SalesContext(
|
context: SalesContext(
|
||||||
market: market,
|
market: market,
|
||||||
|
@ -110,7 +110,6 @@ proc remove(sales: Sales, agent: SalesAgent) {.async.} =
|
||||||
|
|
||||||
proc cleanUp(sales: Sales,
|
proc cleanUp(sales: Sales,
|
||||||
agent: SalesAgent,
|
agent: SalesAgent,
|
||||||
returnBytes: bool,
|
|
||||||
processing: Future[void]) {.async.} =
|
processing: Future[void]) {.async.} =
|
||||||
|
|
||||||
let data = agent.data
|
let data = agent.data
|
||||||
|
@ -121,20 +120,6 @@ proc cleanUp(sales: Sales,
|
||||||
reservationId = data.reservation.?id |? ReservationId.default,
|
reservationId = data.reservation.?id |? ReservationId.default,
|
||||||
availabilityId = data.reservation.?availabilityId |? AvailabilityId.default
|
availabilityId = data.reservation.?availabilityId |? AvailabilityId.default
|
||||||
|
|
||||||
# if reservation for the SalesAgent was not created, then it means
|
|
||||||
# that the cleanUp was called before the sales process really started, so
|
|
||||||
# there are not really any bytes to be returned
|
|
||||||
if returnBytes and request =? data.request and reservation =? data.reservation:
|
|
||||||
if returnErr =? (await sales.context.reservations.returnBytesToAvailability(
|
|
||||||
reservation.availabilityId,
|
|
||||||
reservation.id,
|
|
||||||
request.ask.slotSize
|
|
||||||
)).errorOption:
|
|
||||||
error "failure returning bytes",
|
|
||||||
error = returnErr.msg,
|
|
||||||
availabilityId = reservation.availabilityId,
|
|
||||||
bytes = request.ask.slotSize
|
|
||||||
|
|
||||||
# delete reservation and return reservation bytes back to the availability
|
# delete reservation and return reservation bytes back to the availability
|
||||||
if reservation =? data.reservation and
|
if reservation =? data.reservation and
|
||||||
deleteErr =? (await sales.context.reservations.deleteReservation(
|
deleteErr =? (await sales.context.reservations.deleteReservation(
|
||||||
|
@ -176,8 +161,8 @@ proc processSlot(sales: Sales, item: SlotQueueItem, done: Future[void]) =
|
||||||
none StorageRequest
|
none StorageRequest
|
||||||
)
|
)
|
||||||
|
|
||||||
agent.onCleanUp = proc (returnBytes = false) {.async.} =
|
agent.onCleanUp = proc () {.async.} =
|
||||||
await sales.cleanUp(agent, returnBytes, done)
|
await sales.cleanUp(agent, done)
|
||||||
|
|
||||||
agent.onFilled = some proc(request: StorageRequest, slotIndex: UInt256) =
|
agent.onFilled = some proc(request: StorageRequest, slotIndex: UInt256) =
|
||||||
sales.filled(request, slotIndex, done)
|
sales.filled(request, slotIndex, done)
|
||||||
|
@ -241,9 +226,9 @@ proc load*(sales: Sales) {.async.} =
|
||||||
slot.slotIndex,
|
slot.slotIndex,
|
||||||
some slot.request)
|
some slot.request)
|
||||||
|
|
||||||
agent.onCleanUp = proc(returnBytes = false) {.async.} =
|
agent.onCleanUp = proc() {.async.} =
|
||||||
let done = newFuture[void]("onCleanUp_Dummy")
|
let done = newFuture[void]("onCleanUp_Dummy")
|
||||||
await sales.cleanUp(agent, returnBytes, done)
|
await sales.cleanUp(agent, done)
|
||||||
await done # completed in sales.cleanUp
|
await done # completed in sales.cleanUp
|
||||||
|
|
||||||
agent.start(SaleUnknown())
|
agent.start(SaleUnknown())
|
||||||
|
|
|
@ -55,6 +55,7 @@ type
|
||||||
ReservationId* = distinct array[32, byte]
|
ReservationId* = distinct array[32, byte]
|
||||||
SomeStorableObject = Availability | Reservation
|
SomeStorableObject = Availability | Reservation
|
||||||
SomeStorableId = AvailabilityId | ReservationId
|
SomeStorableId = AvailabilityId | ReservationId
|
||||||
|
|
||||||
Availability* = ref object
|
Availability* = ref object
|
||||||
id* {.serialize.}: AvailabilityId
|
id* {.serialize.}: AvailabilityId
|
||||||
totalSize* {.serialize.}: UInt256
|
totalSize* {.serialize.}: UInt256
|
||||||
|
@ -62,15 +63,24 @@ type
|
||||||
duration* {.serialize.}: UInt256
|
duration* {.serialize.}: UInt256
|
||||||
minPrice* {.serialize.}: UInt256
|
minPrice* {.serialize.}: UInt256
|
||||||
maxCollateral* {.serialize.}: UInt256
|
maxCollateral* {.serialize.}: UInt256
|
||||||
|
# 0 means non-restricted, otherwise contains timestamp until the Availability will be renewed
|
||||||
|
until* {.serialize.}: SecondsSince1970
|
||||||
|
# false means that the availability won't be immidiatelly considered for sale
|
||||||
|
enabled* {.serialize.}: bool
|
||||||
|
|
||||||
Reservation* = ref object
|
Reservation* = ref object
|
||||||
id* {.serialize.}: ReservationId
|
id* {.serialize.}: ReservationId
|
||||||
availabilityId* {.serialize.}: AvailabilityId
|
availabilityId* {.serialize.}: AvailabilityId
|
||||||
size* {.serialize.}: UInt256
|
reservedSize* {.serialize.}: UInt256
|
||||||
|
totalSize* {.serialize.}: UInt256
|
||||||
requestId* {.serialize.}: RequestId
|
requestId* {.serialize.}: RequestId
|
||||||
slotIndex* {.serialize.}: UInt256
|
slotIndex* {.serialize.}: UInt256
|
||||||
|
|
||||||
Reservations* = ref object
|
Reservations* = ref object
|
||||||
repo: RepoStore
|
repo: RepoStore
|
||||||
|
clock: Clock
|
||||||
onAvailabilityAdded: ?OnAvailabilityAdded
|
onAvailabilityAdded: ?OnAvailabilityAdded
|
||||||
|
|
||||||
GetNext* = proc(): Future[?seq[byte]] {.upraises: [], gcsafe, closure.}
|
GetNext* = proc(): Future[?seq[byte]] {.upraises: [], gcsafe, closure.}
|
||||||
OnAvailabilityAdded* = proc(availability: Availability): Future[void] {.upraises: [], gcsafe.}
|
OnAvailabilityAdded* = proc(availability: Availability): Future[void] {.upraises: [], gcsafe.}
|
||||||
StorableIter* = ref object
|
StorableIter* = ref object
|
||||||
|
@ -91,9 +101,10 @@ const
|
||||||
ReservationsKey = (SalesKey / "reservations").tryGet
|
ReservationsKey = (SalesKey / "reservations").tryGet
|
||||||
|
|
||||||
proc new*(T: type Reservations,
|
proc new*(T: type Reservations,
|
||||||
repo: RepoStore): Reservations =
|
repo: RepoStore,
|
||||||
|
clock: Clock): Reservations =
|
||||||
|
|
||||||
T(repo: repo)
|
T(repo: repo, clock: clock)
|
||||||
|
|
||||||
proc init*(
|
proc init*(
|
||||||
_: type Availability,
|
_: type Availability,
|
||||||
|
@ -110,14 +121,15 @@ proc init*(
|
||||||
proc init*(
|
proc init*(
|
||||||
_: type Reservation,
|
_: type Reservation,
|
||||||
availabilityId: AvailabilityId,
|
availabilityId: AvailabilityId,
|
||||||
size: UInt256,
|
totalSize: UInt256,
|
||||||
|
reservedSize: UInt256,
|
||||||
requestId: RequestId,
|
requestId: RequestId,
|
||||||
slotIndex: UInt256
|
slotIndex: UInt256
|
||||||
): Reservation =
|
): Reservation =
|
||||||
|
|
||||||
var id: array[32, byte]
|
var id: array[32, byte]
|
||||||
doAssert randomBytes(id) == 32
|
doAssert randomBytes(id) == 32
|
||||||
Reservation(id: ReservationId(id), availabilityId: availabilityId, size: size, requestId: requestId, slotIndex: slotIndex)
|
Reservation(id: ReservationId(id), availabilityId: availabilityId, totalSize: totalSize, reservedSize: reservedSize, requestId: requestId, slotIndex: slotIndex)
|
||||||
|
|
||||||
func toArray(id: SomeStorableId): array[32, byte] =
|
func toArray(id: SomeStorableId): array[32, byte] =
|
||||||
array[32, byte](id)
|
array[32, byte](id)
|
||||||
|
@ -168,8 +180,7 @@ proc exists*(
|
||||||
self: Reservations,
|
self: Reservations,
|
||||||
key: Key): Future[bool] {.async.} =
|
key: Key): Future[bool] {.async.} =
|
||||||
|
|
||||||
let exists = await self.repo.metaDs.contains(key)
|
return await self.repo.metaDs.contains(key)
|
||||||
return exists
|
|
||||||
|
|
||||||
proc getImpl(
|
proc getImpl(
|
||||||
self: Reservations,
|
self: Reservations,
|
||||||
|
@ -280,17 +291,17 @@ proc deleteReservation*(
|
||||||
else:
|
else:
|
||||||
return failure(error)
|
return failure(error)
|
||||||
|
|
||||||
if reservation.size > 0.u256:
|
|
||||||
trace "returning remaining reservation bytes to availability",
|
|
||||||
size = reservation.size
|
|
||||||
|
|
||||||
without availabilityKey =? availabilityId.key, error:
|
without availabilityKey =? availabilityId.key, error:
|
||||||
return failure(error)
|
return failure(error)
|
||||||
|
|
||||||
without var availability =? await self.get(availabilityKey, Availability), error:
|
without var availability =? await self.get(availabilityKey, Availability), error:
|
||||||
return failure(error)
|
return failure(error)
|
||||||
|
|
||||||
availability.freeSize += reservation.size
|
if reservation.reservedSize > 0.u256:
|
||||||
|
trace "returning remaining reservation bytes to availability",
|
||||||
|
size = reservation.reservedSize
|
||||||
|
|
||||||
|
availability.freeSize += reservation.reservedSize
|
||||||
|
|
||||||
if updateErr =? (await self.update(availability)).errorOption:
|
if updateErr =? (await self.update(availability)).errorOption:
|
||||||
return failure(updateErr)
|
return failure(updateErr)
|
||||||
|
@ -305,12 +316,14 @@ proc createAvailability*(
|
||||||
size: UInt256,
|
size: UInt256,
|
||||||
duration: UInt256,
|
duration: UInt256,
|
||||||
minPrice: UInt256,
|
minPrice: UInt256,
|
||||||
maxCollateral: UInt256): Future[?!Availability] {.async.} =
|
maxCollateral: UInt256,
|
||||||
|
until: SecondsSince1970 = 0,
|
||||||
|
enabled = true): Future[?!Availability] {.async.} =
|
||||||
|
|
||||||
trace "creating availability", size, duration, minPrice, maxCollateral
|
trace "creating availability", size, duration, minPrice, maxCollateral
|
||||||
|
|
||||||
let availability = Availability.init(
|
let availability = Availability.init(
|
||||||
size, size, duration, minPrice, maxCollateral
|
size, size, duration, minPrice, maxCollateral, until, enabled
|
||||||
)
|
)
|
||||||
let bytes = availability.freeSize.truncate(uint)
|
let bytes = availability.freeSize.truncate(uint)
|
||||||
|
|
||||||
|
@ -327,7 +340,8 @@ proc createAvailability*(
|
||||||
|
|
||||||
return failure(updateErr)
|
return failure(updateErr)
|
||||||
|
|
||||||
if onAvailabilityAdded =? self.onAvailabilityAdded:
|
# we won't trigger the callback if the availability is not enabled
|
||||||
|
if enabled and onAvailabilityAdded =? self.onAvailabilityAdded:
|
||||||
try:
|
try:
|
||||||
await onAvailabilityAdded(availability)
|
await onAvailabilityAdded(availability)
|
||||||
except CatchableError as e:
|
except CatchableError as e:
|
||||||
|
@ -348,7 +362,7 @@ proc createReservation*(
|
||||||
|
|
||||||
trace "creating reservation", availabilityId, slotSize, requestId, slotIndex
|
trace "creating reservation", availabilityId, slotSize, requestId, slotIndex
|
||||||
|
|
||||||
let reservation = Reservation.init(availabilityId, slotSize, requestId, slotIndex)
|
let reservation = Reservation.init(availabilityId, slotSize, slotSize, requestId, slotIndex)
|
||||||
|
|
||||||
without availabilityKey =? availabilityId.key, error:
|
without availabilityKey =? availabilityId.key, error:
|
||||||
return failure(error)
|
return failure(error)
|
||||||
|
@ -397,7 +411,6 @@ proc returnBytesToAvailability*(
|
||||||
reservationId
|
reservationId
|
||||||
availabilityId
|
availabilityId
|
||||||
|
|
||||||
|
|
||||||
without key =? key(reservationId, availabilityId), error:
|
without key =? key(reservationId, availabilityId), error:
|
||||||
return failure(error)
|
return failure(error)
|
||||||
|
|
||||||
|
@ -406,7 +419,7 @@ proc returnBytesToAvailability*(
|
||||||
|
|
||||||
# We are ignoring bytes that are still present in the Reservation because
|
# We are ignoring bytes that are still present in the Reservation because
|
||||||
# they will be returned to Availability through `deleteReservation`.
|
# they will be returned to Availability through `deleteReservation`.
|
||||||
let bytesToBeReturned = bytes - reservation.size
|
let bytesToBeReturned = bytes - reservation.reservedSize
|
||||||
|
|
||||||
if bytesToBeReturned == 0:
|
if bytesToBeReturned == 0:
|
||||||
trace "No bytes are returned", requestSizeBytes = bytes, returningBytes = bytesToBeReturned
|
trace "No bytes are returned", requestSizeBytes = bytes, returningBytes = bytesToBeReturned
|
||||||
|
@ -459,7 +472,7 @@ proc release*(
|
||||||
without var reservation =? (await self.get(key, Reservation)), error:
|
without var reservation =? (await self.get(key, Reservation)), error:
|
||||||
return failure(error)
|
return failure(error)
|
||||||
|
|
||||||
if reservation.size < bytes.u256:
|
if reservation.reservedSize < bytes.u256:
|
||||||
let error = newException(
|
let error = newException(
|
||||||
BytesOutOfBoundsError,
|
BytesOutOfBoundsError,
|
||||||
"trying to release an amount of bytes that is greater than the total size of the Reservation")
|
"trying to release an amount of bytes that is greater than the total size of the Reservation")
|
||||||
|
@ -468,7 +481,7 @@ proc release*(
|
||||||
if releaseErr =? (await self.repo.release(bytes)).errorOption:
|
if releaseErr =? (await self.repo.release(bytes)).errorOption:
|
||||||
return failure(releaseErr.toErr(ReleaseFailedError))
|
return failure(releaseErr.toErr(ReleaseFailedError))
|
||||||
|
|
||||||
reservation.size -= bytes.u256
|
reservation.reservedSize -= bytes.u256
|
||||||
|
|
||||||
# persist partially used Reservation with updated size
|
# persist partially used Reservation with updated size
|
||||||
if err =? (await self.update(reservation)).errorOption:
|
if err =? (await self.update(reservation)).errorOption:
|
||||||
|
|
|
@ -25,7 +25,7 @@ type
|
||||||
onCleanUp*: OnCleanUp
|
onCleanUp*: OnCleanUp
|
||||||
onFilled*: ?OnFilled
|
onFilled*: ?OnFilled
|
||||||
|
|
||||||
OnCleanUp* = proc (returnBytes = false): Future[void] {.gcsafe, upraises: [].}
|
OnCleanUp* = proc (): Future[void] {.gcsafe, upraises: [].}
|
||||||
OnFilled* = proc(request: StorageRequest,
|
OnFilled* = proc(request: StorageRequest,
|
||||||
slotIndex: UInt256) {.gcsafe, upraises: [].}
|
slotIndex: UInt256) {.gcsafe, upraises: [].}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,6 @@ method run*(state: SaleCancelled, machine: Machine): Future[?State] {.async.} =
|
||||||
onClear(request, data.slotIndex)
|
onClear(request, data.slotIndex)
|
||||||
|
|
||||||
if onCleanUp =? agent.onCleanUp:
|
if onCleanUp =? agent.onCleanUp:
|
||||||
await onCleanUp(returnBytes = true)
|
await onCleanUp()
|
||||||
|
|
||||||
warn "Sale cancelled due to timeout", requestId = data.requestId, slotIndex = data.slotIndex
|
warn "Sale cancelled due to timeout", requestId = data.requestId, slotIndex = data.slotIndex
|
||||||
|
|
|
@ -72,4 +72,11 @@ method run*(state: SaleDownloading, machine: Machine): Future[?State] {.async.}
|
||||||
return some State(SaleErrored(error: err))
|
return some State(SaleErrored(error: err))
|
||||||
|
|
||||||
trace "Download complete"
|
trace "Download complete"
|
||||||
|
|
||||||
|
if updatedReservation =? await reservations.get(reservation.id, Reservation):
|
||||||
|
if updatedReservation.size != 0:
|
||||||
|
error "After downloading the data there is unused capacity in Reservation"
|
||||||
|
else:
|
||||||
|
error "Couldn't get updated reservation"
|
||||||
|
|
||||||
return some State(SaleInitialProving())
|
return some State(SaleInitialProving())
|
||||||
|
|
|
@ -30,5 +30,5 @@ method run*(state: SaleErrored, machine: Machine): Future[?State] {.async.} =
|
||||||
onClear(request, data.slotIndex)
|
onClear(request, data.slotIndex)
|
||||||
|
|
||||||
if onCleanUp =? agent.onCleanUp:
|
if onCleanUp =? agent.onCleanUp:
|
||||||
await onCleanUp(returnBytes = true)
|
await onCleanUp()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue