mirror of
https://github.com/logos-storage/logos-storage-nim.git
synced 2026-01-04 06:23:06 +00:00
Feat: price per byte (#1078)
* openAPI: StorageRequestCreation: reward => pricePerByte, collateral => collateralPerByte * purchasing: reward => pricePerByte, collateral => collateralPerByte * Updates availabilities and reservations to use totalCollateral, minPricePerByte, and maxCollateralPerByte * Uses correct div operator when operating on UInt256 * proposal updating totalCollateral in availability * makes sure that reading currentCollateral happens before freeing slot * Updates naming * fixes tests: unit and contracts * uses feat/price-per-byte branch for codex-contracts-eth * temporarily disables integration tests on CI * introduces high level <<totalCollateral>> property for a cleaner external interface * updates integration tests * Applies review comments * Updates description of totalCollateral in SalesAvailability * updates codex-contracts-eth (price-per-byte)
This commit is contained in:
parent
f6c792de79
commit
962fc1cd95
@ -106,7 +106,7 @@ method mySlots*(market: OnChainMarket): Future[seq[SlotId]] {.async.} =
|
|||||||
method requestStorage(market: OnChainMarket, request: StorageRequest) {.async.} =
|
method requestStorage(market: OnChainMarket, request: StorageRequest) {.async.} =
|
||||||
convertEthersError:
|
convertEthersError:
|
||||||
debug "Requesting storage"
|
debug "Requesting storage"
|
||||||
await market.approveFunds(request.price())
|
await market.approveFunds(request.totalPrice())
|
||||||
discard await market.contract.requestStorage(request).confirm(1)
|
discard await market.contract.requestStorage(request).confirm(1)
|
||||||
|
|
||||||
method getRequest*(
|
method getRequest*(
|
||||||
@ -156,6 +156,12 @@ method getHost(
|
|||||||
else:
|
else:
|
||||||
return none Address
|
return none Address
|
||||||
|
|
||||||
|
method currentCollateral*(
|
||||||
|
market: OnChainMarket, slotId: SlotId
|
||||||
|
): Future[UInt256] {.async.} =
|
||||||
|
convertEthersError:
|
||||||
|
return await market.contract.currentCollateral(slotId)
|
||||||
|
|
||||||
method getActiveSlot*(market: OnChainMarket, slotId: SlotId): Future[?Slot] {.async.} =
|
method getActiveSlot*(market: OnChainMarket, slotId: SlotId): Future[?Slot] {.async.} =
|
||||||
convertEthersError:
|
convertEthersError:
|
||||||
try:
|
try:
|
||||||
|
|||||||
@ -48,6 +48,10 @@ type
|
|||||||
|
|
||||||
proc configuration*(marketplace: Marketplace): MarketplaceConfig {.contract, view.}
|
proc configuration*(marketplace: Marketplace): MarketplaceConfig {.contract, view.}
|
||||||
proc token*(marketplace: Marketplace): Address {.contract, view.}
|
proc token*(marketplace: Marketplace): Address {.contract, view.}
|
||||||
|
proc currentCollateral*(
|
||||||
|
marketplace: Marketplace, id: SlotId
|
||||||
|
): UInt256 {.contract, view.}
|
||||||
|
|
||||||
proc slashMisses*(marketplace: Marketplace): UInt256 {.contract, view.}
|
proc slashMisses*(marketplace: Marketplace): UInt256 {.contract, view.}
|
||||||
proc slashPercentage*(marketplace: Marketplace): UInt256 {.contract, view.}
|
proc slashPercentage*(marketplace: Marketplace): UInt256 {.contract, view.}
|
||||||
proc minCollateralThreshold*(marketplace: Marketplace): UInt256 {.contract, view.}
|
proc minCollateralThreshold*(marketplace: Marketplace): UInt256 {.contract, view.}
|
||||||
|
|||||||
@ -24,8 +24,8 @@ type
|
|||||||
slotSize* {.serialize.}: UInt256
|
slotSize* {.serialize.}: UInt256
|
||||||
duration* {.serialize.}: UInt256
|
duration* {.serialize.}: UInt256
|
||||||
proofProbability* {.serialize.}: UInt256
|
proofProbability* {.serialize.}: UInt256
|
||||||
reward* {.serialize.}: UInt256
|
pricePerBytePerSecond* {.serialize.}: UInt256
|
||||||
collateral* {.serialize.}: UInt256
|
collateralPerByte* {.serialize.}: UInt256
|
||||||
maxSlotLoss* {.serialize.}: uint64
|
maxSlotLoss* {.serialize.}: uint64
|
||||||
|
|
||||||
StorageContent* = object
|
StorageContent* = object
|
||||||
@ -112,8 +112,8 @@ func fromTuple(_: type StorageAsk, tupl: tuple): StorageAsk =
|
|||||||
slotSize: tupl[1],
|
slotSize: tupl[1],
|
||||||
duration: tupl[2],
|
duration: tupl[2],
|
||||||
proofProbability: tupl[3],
|
proofProbability: tupl[3],
|
||||||
reward: tupl[4],
|
pricePerBytePerSecond: tupl[4],
|
||||||
collateral: tupl[5],
|
collateralPerByte: tupl[5],
|
||||||
maxSlotLoss: tupl[6],
|
maxSlotLoss: tupl[6],
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -174,14 +174,20 @@ func slotId*(request: StorageRequest, slotIndex: UInt256): SlotId =
|
|||||||
func id*(slot: Slot): SlotId =
|
func id*(slot: Slot): SlotId =
|
||||||
slotId(slot.request, slot.slotIndex)
|
slotId(slot.request, slot.slotIndex)
|
||||||
|
|
||||||
func pricePerSlot*(ask: StorageAsk): UInt256 =
|
func pricePerSlotPerSecond*(ask: StorageAsk): UInt256 =
|
||||||
ask.duration * ask.reward
|
ask.pricePerBytePerSecond * ask.slotSize
|
||||||
|
|
||||||
func price*(ask: StorageAsk): UInt256 =
|
func pricePerSlot*(ask: StorageAsk): UInt256 =
|
||||||
|
ask.duration * ask.pricePerSlotPerSecond
|
||||||
|
|
||||||
|
func totalPrice*(ask: StorageAsk): UInt256 =
|
||||||
ask.slots.u256 * ask.pricePerSlot
|
ask.slots.u256 * ask.pricePerSlot
|
||||||
|
|
||||||
func price*(request: StorageRequest): UInt256 =
|
func totalPrice*(request: StorageRequest): UInt256 =
|
||||||
request.ask.price
|
request.ask.totalPrice
|
||||||
|
|
||||||
|
func collateralPerSlot*(ask: StorageAsk): UInt256 =
|
||||||
|
ask.collateralPerByte * ask.slotSize
|
||||||
|
|
||||||
func size*(ask: StorageAsk): UInt256 =
|
func size*(ask: StorageAsk): UInt256 =
|
||||||
ask.slots.u256 * ask.slotSize
|
ask.slots.u256 * ask.slotSize
|
||||||
|
|||||||
@ -126,6 +126,11 @@ method getHost*(
|
|||||||
): Future[?Address] {.base, async.} =
|
): Future[?Address] {.base, async.} =
|
||||||
raiseAssert("not implemented")
|
raiseAssert("not implemented")
|
||||||
|
|
||||||
|
method currentCollateral*(
|
||||||
|
market: Market, slotId: SlotId
|
||||||
|
): Future[UInt256] {.base, async.} =
|
||||||
|
raiseAssert("not implemented")
|
||||||
|
|
||||||
method getActiveSlot*(market: Market, slotId: SlotId): Future[?Slot] {.base, async.} =
|
method getActiveSlot*(market: Market, slotId: SlotId): Future[?Slot] {.base, async.} =
|
||||||
raiseAssert("not implemented")
|
raiseAssert("not implemented")
|
||||||
|
|
||||||
|
|||||||
@ -373,8 +373,8 @@ proc setupRequest(
|
|||||||
proofProbability: UInt256,
|
proofProbability: UInt256,
|
||||||
nodes: uint,
|
nodes: uint,
|
||||||
tolerance: uint,
|
tolerance: uint,
|
||||||
reward: UInt256,
|
pricePerBytePerSecond: UInt256,
|
||||||
collateral: UInt256,
|
collateralPerByte: UInt256,
|
||||||
expiry: UInt256,
|
expiry: UInt256,
|
||||||
): Future[?!StorageRequest] {.async.} =
|
): Future[?!StorageRequest] {.async.} =
|
||||||
## Setup slots for a given dataset
|
## Setup slots for a given dataset
|
||||||
@ -389,9 +389,9 @@ proc setupRequest(
|
|||||||
duration = duration
|
duration = duration
|
||||||
nodes = nodes
|
nodes = nodes
|
||||||
tolerance = tolerance
|
tolerance = tolerance
|
||||||
reward = reward
|
pricePerBytePerSecond = pricePerBytePerSecond
|
||||||
proofProbability = proofProbability
|
proofProbability = proofProbability
|
||||||
collateral = collateral
|
collateralPerByte = collateralPerByte
|
||||||
expiry = expiry
|
expiry = expiry
|
||||||
ecK = ecK
|
ecK = ecK
|
||||||
ecM = ecM
|
ecM = ecM
|
||||||
@ -435,8 +435,8 @@ proc setupRequest(
|
|||||||
slotSize: builder.slotBytes.uint.u256,
|
slotSize: builder.slotBytes.uint.u256,
|
||||||
duration: duration,
|
duration: duration,
|
||||||
proofProbability: proofProbability,
|
proofProbability: proofProbability,
|
||||||
reward: reward,
|
pricePerBytePerSecond: pricePerBytePerSecond,
|
||||||
collateral: collateral,
|
collateralPerByte: collateralPerByte,
|
||||||
maxSlotLoss: tolerance,
|
maxSlotLoss: tolerance,
|
||||||
),
|
),
|
||||||
content: StorageContent(
|
content: StorageContent(
|
||||||
@ -456,8 +456,8 @@ proc requestStorage*(
|
|||||||
proofProbability: UInt256,
|
proofProbability: UInt256,
|
||||||
nodes: uint,
|
nodes: uint,
|
||||||
tolerance: uint,
|
tolerance: uint,
|
||||||
reward: UInt256,
|
pricePerBytePerSecond: UInt256,
|
||||||
collateral: UInt256,
|
collateralPerByte: UInt256,
|
||||||
expiry: UInt256,
|
expiry: UInt256,
|
||||||
): Future[?!PurchaseId] {.async.} =
|
): Future[?!PurchaseId] {.async.} =
|
||||||
## Initiate a request for storage sequence, this might
|
## Initiate a request for storage sequence, this might
|
||||||
@ -469,9 +469,9 @@ proc requestStorage*(
|
|||||||
duration = duration
|
duration = duration
|
||||||
nodes = nodes
|
nodes = nodes
|
||||||
tolerance = tolerance
|
tolerance = tolerance
|
||||||
reward = reward
|
pricePerBytePerSecond = pricePerBytePerSecond
|
||||||
proofProbability = proofProbability
|
proofProbability = proofProbability
|
||||||
collateral = collateral
|
collateralPerByte = collateralPerByte
|
||||||
expiry = expiry.truncate(int64)
|
expiry = expiry.truncate(int64)
|
||||||
now = self.clock.now
|
now = self.clock.now
|
||||||
|
|
||||||
@ -483,7 +483,8 @@ proc requestStorage*(
|
|||||||
|
|
||||||
without request =? (
|
without request =? (
|
||||||
await self.setupRequest(
|
await self.setupRequest(
|
||||||
cid, duration, proofProbability, nodes, tolerance, reward, collateral, expiry
|
cid, duration, proofProbability, nodes, tolerance, pricePerBytePerSecond,
|
||||||
|
collateralPerByte, expiry,
|
||||||
)
|
)
|
||||||
), err:
|
), err:
|
||||||
trace "Unable to setup request"
|
trace "Unable to setup request"
|
||||||
|
|||||||
@ -403,12 +403,15 @@ proc initSalesApi(node: CodexNodeRef, router: var RestRouter) =
|
|||||||
|
|
||||||
router.rawApi(MethodPost, "/api/codex/v1/sales/availability") do() -> RestApiResponse:
|
router.rawApi(MethodPost, "/api/codex/v1/sales/availability") do() -> RestApiResponse:
|
||||||
## Add available storage to sell.
|
## Add available storage to sell.
|
||||||
## Every time Availability's offer finishes, its capacity is returned to the availability.
|
## Every time Availability's offer finishes, its capacity is
|
||||||
|
## returned to the availability.
|
||||||
##
|
##
|
||||||
## totalSize - size of available storage in bytes
|
## totalSize - size of available storage in bytes
|
||||||
## duration - maximum time the storage should be sold for (in seconds)
|
## duration - maximum time the storage should be sold for (in seconds)
|
||||||
## minPrice - minimal price paid (in amount of tokens) for the whole hosted request's slot for the request's duration
|
## minPricePerBytePerSecond - minimal price per byte paid (in amount of
|
||||||
## maxCollateral - maximum collateral user is willing to pay per filled Slot (in amount of tokens)
|
## tokens) to be matched against the request's pricePerBytePerSecond
|
||||||
|
## totalCollateral - total collateral (in amount of
|
||||||
|
## tokens) that can be distributed among matching requests
|
||||||
|
|
||||||
var headers = buildCorsHeaders("POST", allowedOrigin)
|
var headers = buildCorsHeaders("POST", allowedOrigin)
|
||||||
|
|
||||||
@ -436,7 +439,8 @@ proc initSalesApi(node: CodexNodeRef, router: var RestRouter) =
|
|||||||
|
|
||||||
without availability =? (
|
without availability =? (
|
||||||
await reservations.createAvailability(
|
await reservations.createAvailability(
|
||||||
restAv.totalSize, restAv.duration, restAv.minPrice, restAv.maxCollateral
|
restAv.totalSize, restAv.duration, restAv.minPricePerBytePerSecond,
|
||||||
|
restAv.totalCollateral,
|
||||||
)
|
)
|
||||||
), error:
|
), error:
|
||||||
return RestApiResponse.error(Http500, error.msg, headers = headers)
|
return RestApiResponse.error(Http500, error.msg, headers = headers)
|
||||||
@ -467,10 +471,14 @@ proc initSalesApi(node: CodexNodeRef, router: var RestRouter) =
|
|||||||
## The new parameters will be only considered for new requests.
|
## The new parameters will be only considered for new requests.
|
||||||
## Existing Requests linked to this Availability will continue as is.
|
## Existing Requests linked to this Availability will continue as is.
|
||||||
##
|
##
|
||||||
## totalSize - size of available storage in bytes. When decreasing the size, then lower limit is the currently `totalSize - freeSize`.
|
## totalSize - size of available storage in bytes.
|
||||||
## duration - maximum time the storage should be sold for (in seconds)
|
## When decreasing the size, then lower limit is
|
||||||
## minPrice - minimum price to be paid (in amount of tokens)
|
## the currently `totalSize - freeSize`.
|
||||||
## maxCollateral - maximum collateral user is willing to pay per filled Slot (in amount of tokens)
|
## duration - maximum time the storage should be sold for (in seconds)
|
||||||
|
## minPricePerBytePerSecond - minimal price per byte paid (in amount of
|
||||||
|
## tokens) to be matched against the request's pricePerBytePerSecond
|
||||||
|
## totalCollateral - total collateral (in amount of
|
||||||
|
## tokens) that can be distributed among matching requests
|
||||||
try:
|
try:
|
||||||
without contracts =? node.contracts.host:
|
without contracts =? node.contracts.host:
|
||||||
return RestApiResponse.error(Http503, "Persistence is not enabled")
|
return RestApiResponse.error(Http503, "Persistence is not enabled")
|
||||||
@ -512,11 +520,11 @@ proc initSalesApi(node: CodexNodeRef, router: var RestRouter) =
|
|||||||
if duration =? restAv.duration:
|
if duration =? restAv.duration:
|
||||||
availability.duration = duration
|
availability.duration = duration
|
||||||
|
|
||||||
if minPrice =? restAv.minPrice:
|
if minPricePerBytePerSecond =? restAv.minPricePerBytePerSecond:
|
||||||
availability.minPrice = minPrice
|
availability.minPricePerBytePerSecond = minPricePerBytePerSecond
|
||||||
|
|
||||||
if maxCollateral =? restAv.maxCollateral:
|
if totalCollateral =? restAv.totalCollateral:
|
||||||
availability.maxCollateral = maxCollateral
|
availability.totalCollateral = totalCollateral
|
||||||
|
|
||||||
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)
|
||||||
@ -580,11 +588,11 @@ proc initPurchasingApi(node: CodexNodeRef, router: var RestRouter) =
|
|||||||
## cid - the cid of a previously uploaded dataset
|
## cid - the cid of a previously uploaded dataset
|
||||||
## duration - the duration of the request in seconds
|
## duration - the duration of the request in seconds
|
||||||
## proofProbability - how often storage proofs are required
|
## proofProbability - how often storage proofs are required
|
||||||
## reward - the maximum amount of tokens paid per second per slot to hosts the client is willing to pay
|
## pricePerBytePerSecond - the amount of tokens paid per byte per second to hosts the client is willing to pay
|
||||||
## expiry - specifies threshold in seconds from now when the request expires if the Request does not find requested amount of nodes to host the data
|
## expiry - specifies threshold in seconds from now when the request expires if the Request does not find requested amount of nodes to host the data
|
||||||
## nodes - number of nodes the content should be stored on
|
## nodes - number of nodes the content should be stored on
|
||||||
## tolerance - allowed number of nodes that can be lost before content is lost
|
## tolerance - allowed number of nodes that can be lost before content is lost
|
||||||
## colateral - requested collateral from hosts when they fill slot
|
## colateralPerByte - requested collateral per byte from hosts when they fill slot
|
||||||
try:
|
try:
|
||||||
without contracts =? node.contracts.client:
|
without contracts =? node.contracts.client:
|
||||||
return RestApiResponse.error(
|
return RestApiResponse.error(
|
||||||
@ -639,7 +647,7 @@ proc initPurchasingApi(node: CodexNodeRef, router: var RestRouter) =
|
|||||||
without purchaseId =?
|
without purchaseId =?
|
||||||
await node.requestStorage(
|
await node.requestStorage(
|
||||||
cid, params.duration, params.proofProbability, nodes, tolerance,
|
cid, params.duration, params.proofProbability, nodes, tolerance,
|
||||||
params.reward, params.collateral, expiry,
|
params.pricePerBytePerSecond, params.collateralPerByte, expiry,
|
||||||
), error:
|
), error:
|
||||||
if error of InsufficientBlocksError:
|
if error of InsufficientBlocksError:
|
||||||
return RestApiResponse.error(
|
return RestApiResponse.error(
|
||||||
|
|||||||
@ -15,8 +15,8 @@ type
|
|||||||
StorageRequestParams* = object
|
StorageRequestParams* = object
|
||||||
duration* {.serialize.}: UInt256
|
duration* {.serialize.}: UInt256
|
||||||
proofProbability* {.serialize.}: UInt256
|
proofProbability* {.serialize.}: UInt256
|
||||||
reward* {.serialize.}: UInt256
|
pricePerBytePerSecond* {.serialize.}: UInt256
|
||||||
collateral* {.serialize.}: UInt256
|
collateralPerByte* {.serialize.}: UInt256
|
||||||
expiry* {.serialize.}: ?UInt256
|
expiry* {.serialize.}: ?UInt256
|
||||||
nodes* {.serialize.}: ?uint
|
nodes* {.serialize.}: ?uint
|
||||||
tolerance* {.serialize.}: ?uint
|
tolerance* {.serialize.}: ?uint
|
||||||
@ -30,8 +30,8 @@ type
|
|||||||
RestAvailability* = object
|
RestAvailability* = object
|
||||||
totalSize* {.serialize.}: UInt256
|
totalSize* {.serialize.}: UInt256
|
||||||
duration* {.serialize.}: UInt256
|
duration* {.serialize.}: UInt256
|
||||||
minPrice* {.serialize.}: UInt256
|
minPricePerBytePerSecond* {.serialize.}: UInt256
|
||||||
maxCollateral* {.serialize.}: UInt256
|
totalCollateral* {.serialize.}: UInt256
|
||||||
freeSize* {.serialize.}: ?UInt256
|
freeSize* {.serialize.}: ?UInt256
|
||||||
|
|
||||||
RestSalesAgent* = object
|
RestSalesAgent* = object
|
||||||
|
|||||||
@ -115,6 +115,7 @@ proc cleanUp(
|
|||||||
agent: SalesAgent,
|
agent: SalesAgent,
|
||||||
returnBytes: bool,
|
returnBytes: bool,
|
||||||
reprocessSlot: bool,
|
reprocessSlot: bool,
|
||||||
|
returnedCollateral: ?UInt256,
|
||||||
processing: Future[void],
|
processing: Future[void],
|
||||||
) {.async.} =
|
) {.async.} =
|
||||||
let data = agent.data
|
let data = agent.data
|
||||||
@ -144,7 +145,7 @@ proc cleanUp(
|
|||||||
if reservation =? data.reservation and
|
if reservation =? data.reservation and
|
||||||
deleteErr =? (
|
deleteErr =? (
|
||||||
await sales.context.reservations.deleteReservation(
|
await sales.context.reservations.deleteReservation(
|
||||||
reservation.id, reservation.availabilityId
|
reservation.id, reservation.availabilityId, returnedCollateral
|
||||||
)
|
)
|
||||||
).errorOption:
|
).errorOption:
|
||||||
error "failure deleting reservation", error = deleteErr.msg
|
error "failure deleting reservation", error = deleteErr.msg
|
||||||
@ -187,8 +188,10 @@ proc processSlot(sales: Sales, item: SlotQueueItem, done: Future[void]) =
|
|||||||
sales.context, item.requestId, item.slotIndex.u256, none StorageRequest
|
sales.context, item.requestId, item.slotIndex.u256, none StorageRequest
|
||||||
)
|
)
|
||||||
|
|
||||||
agent.onCleanUp = proc(returnBytes = false, reprocessSlot = false) {.async.} =
|
agent.onCleanUp = proc(
|
||||||
await sales.cleanUp(agent, returnBytes, reprocessSlot, done)
|
returnBytes = false, reprocessSlot = false, returnedCollateral = UInt256.none
|
||||||
|
) {.async.} =
|
||||||
|
await sales.cleanUp(agent, returnBytes, reprocessSlot, returnedCollateral, 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)
|
||||||
@ -253,11 +256,13 @@ proc load*(sales: Sales) {.async.} =
|
|||||||
let agent =
|
let agent =
|
||||||
newSalesAgent(sales.context, slot.request.id, slot.slotIndex, some slot.request)
|
newSalesAgent(sales.context, slot.request.id, slot.slotIndex, some slot.request)
|
||||||
|
|
||||||
agent.onCleanUp = proc(returnBytes = false, reprocessSlot = false) {.async.} =
|
agent.onCleanUp = proc(
|
||||||
|
returnBytes = false, reprocessSlot = false, returnedCollateral = UInt256.none
|
||||||
|
) {.async.} =
|
||||||
# since workers are not being dispatched, this future has not been created
|
# since workers are not being dispatched, this future has not been created
|
||||||
# by a worker. Create a dummy one here so we can call sales.cleanUp
|
# by a worker. Create a dummy one here so we can call sales.cleanUp
|
||||||
let done: Future[void] = nil
|
let done: Future[void] = nil
|
||||||
await sales.cleanUp(agent, returnBytes, reprocessSlot, done)
|
await sales.cleanUp(agent, returnBytes, reprocessSlot, returnedCollateral, done)
|
||||||
|
|
||||||
# There is no need to assign agent.onFilled as slots loaded from `mySlots`
|
# There is no need to assign agent.onFilled as slots loaded from `mySlots`
|
||||||
# are inherently already filled and so assigning agent.onFilled would be
|
# are inherently already filled and so assigning agent.onFilled would be
|
||||||
|
|||||||
@ -7,23 +7,25 @@
|
|||||||
## This file may not be copied, modified, or distributed except according to
|
## This file may not be copied, modified, or distributed except according to
|
||||||
## those terms.
|
## those terms.
|
||||||
##
|
##
|
||||||
## +--------------------------------------+
|
## +--------------------------------------+
|
||||||
## | RESERVATION |
|
## | RESERVATION |
|
||||||
## +----------------------------------------+ |--------------------------------------|
|
## +---------------------------------------------------+ |--------------------------------------|
|
||||||
## | AVAILABILITY | | ReservationId | id | PK |
|
## | AVAILABILITY | | ReservationId | id | PK |
|
||||||
## |----------------------------------------| |--------------------------------------|
|
## |---------------------------------------------------| |--------------------------------------|
|
||||||
## | AvailabilityId | id | PK |<-||-------o<-| AvailabilityId | availabilityId | FK |
|
## | AvailabilityId | id | PK |<-||-------o<-| AvailabilityId | availabilityId | FK |
|
||||||
## |----------------------------------------| |--------------------------------------|
|
## |---------------------------------------------------| |--------------------------------------|
|
||||||
## | UInt256 | totalSize | | | UInt256 | size | |
|
## | UInt256 | totalSize | | | UInt256 | size | |
|
||||||
## |----------------------------------------| |--------------------------------------|
|
## |---------------------------------------------------| |--------------------------------------|
|
||||||
## | UInt256 | freeSize | | | UInt256 | slotIndex | |
|
## | UInt256 | freeSize | | | UInt256 | slotIndex | |
|
||||||
## |----------------------------------------| +--------------------------------------+
|
## |---------------------------------------------------| +--------------------------------------+
|
||||||
## | UInt256 | duration | |
|
## | UInt256 | duration | |
|
||||||
## |----------------------------------------|
|
## |---------------------------------------------------|
|
||||||
## | UInt256 | minPrice | |
|
## | UInt256 | minPricePerBytePerSecond | |
|
||||||
## |----------------------------------------|
|
## |---------------------------------------------------|
|
||||||
## | UInt256 | maxCollateral | |
|
## | UInt256 | totalCollateral | |
|
||||||
## +----------------------------------------+
|
## |---------------------------------------------------|
|
||||||
|
## | UInt256 | totalRemainingCollateral | |
|
||||||
|
## +---------------------------------------------------+
|
||||||
|
|
||||||
import pkg/upraises
|
import pkg/upraises
|
||||||
push:
|
push:
|
||||||
@ -65,9 +67,9 @@ type
|
|||||||
totalSize* {.serialize.}: UInt256
|
totalSize* {.serialize.}: UInt256
|
||||||
freeSize* {.serialize.}: UInt256
|
freeSize* {.serialize.}: UInt256
|
||||||
duration* {.serialize.}: UInt256
|
duration* {.serialize.}: UInt256
|
||||||
minPrice* {.serialize.}: UInt256
|
minPricePerBytePerSecond* {.serialize.}: UInt256
|
||||||
# minimal price paid for the whole hosted slot for the request's duration
|
totalCollateral {.serialize.}: UInt256
|
||||||
maxCollateral* {.serialize.}: UInt256
|
totalRemainingCollateral* {.serialize.}: UInt256
|
||||||
|
|
||||||
Reservation* = ref object
|
Reservation* = ref object
|
||||||
id* {.serialize.}: ReservationId
|
id* {.serialize.}: ReservationId
|
||||||
@ -124,8 +126,8 @@ proc init*(
|
|||||||
totalSize: UInt256,
|
totalSize: UInt256,
|
||||||
freeSize: UInt256,
|
freeSize: UInt256,
|
||||||
duration: UInt256,
|
duration: UInt256,
|
||||||
minPrice: UInt256,
|
minPricePerBytePerSecond: UInt256,
|
||||||
maxCollateral: UInt256,
|
totalCollateral: UInt256,
|
||||||
): Availability =
|
): Availability =
|
||||||
var id: array[32, byte]
|
var id: array[32, byte]
|
||||||
doAssert randomBytes(id) == 32
|
doAssert randomBytes(id) == 32
|
||||||
@ -134,10 +136,18 @@ proc init*(
|
|||||||
totalSize: totalSize,
|
totalSize: totalSize,
|
||||||
freeSize: freeSize,
|
freeSize: freeSize,
|
||||||
duration: duration,
|
duration: duration,
|
||||||
minPrice: minPrice,
|
minPricePerBytePerSecond: minPricePerBytePerSecond,
|
||||||
maxCollateral: maxCollateral,
|
totalCollateral: totalCollateral,
|
||||||
|
totalRemainingCollateral: totalCollateral,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func totalCollateral*(self: Availability): UInt256 {.inline.} =
|
||||||
|
return self.totalCollateral
|
||||||
|
|
||||||
|
proc `totalCollateral=`*(self: Availability, value: UInt256) {.inline.} =
|
||||||
|
self.totalCollateral = value
|
||||||
|
self.totalRemainingCollateral = value
|
||||||
|
|
||||||
proc init*(
|
proc init*(
|
||||||
_: type Reservation,
|
_: type Reservation,
|
||||||
availabilityId: AvailabilityId,
|
availabilityId: AvailabilityId,
|
||||||
@ -195,6 +205,9 @@ func key*(reservationId: ReservationId, availabilityId: AvailabilityId): ?!Key =
|
|||||||
func key*(availability: Availability): ?!Key =
|
func key*(availability: Availability): ?!Key =
|
||||||
return availability.id.key
|
return availability.id.key
|
||||||
|
|
||||||
|
func maxCollateralPerByte*(availability: Availability): UInt256 =
|
||||||
|
return availability.totalRemainingCollateral div availability.freeSize
|
||||||
|
|
||||||
func key*(reservation: Reservation): ?!Key =
|
func key*(reservation: Reservation): ?!Key =
|
||||||
return key(reservation.id, reservation.availabilityId)
|
return key(reservation.id, reservation.availabilityId)
|
||||||
|
|
||||||
@ -328,7 +341,10 @@ proc delete(self: Reservations, key: Key): Future[?!void] {.async.} =
|
|||||||
return success()
|
return success()
|
||||||
|
|
||||||
proc deleteReservation*(
|
proc deleteReservation*(
|
||||||
self: Reservations, reservationId: ReservationId, availabilityId: AvailabilityId
|
self: Reservations,
|
||||||
|
reservationId: ReservationId,
|
||||||
|
availabilityId: AvailabilityId,
|
||||||
|
returnedCollateral: ?UInt256 = UInt256.none,
|
||||||
): Future[?!void] {.async.} =
|
): Future[?!void] {.async.} =
|
||||||
logScope:
|
logScope:
|
||||||
reservationId
|
reservationId
|
||||||
@ -357,6 +373,9 @@ proc deleteReservation*(
|
|||||||
|
|
||||||
availability.freeSize += reservation.size
|
availability.freeSize += reservation.size
|
||||||
|
|
||||||
|
if collateral =? returnedCollateral:
|
||||||
|
availability.totalRemainingCollateral += collateral
|
||||||
|
|
||||||
if updateErr =? (await self.updateAvailability(availability)).errorOption:
|
if updateErr =? (await self.updateAvailability(availability)).errorOption:
|
||||||
return failure(updateErr)
|
return failure(updateErr)
|
||||||
|
|
||||||
@ -372,12 +391,14 @@ proc createAvailability*(
|
|||||||
self: Reservations,
|
self: Reservations,
|
||||||
size: UInt256,
|
size: UInt256,
|
||||||
duration: UInt256,
|
duration: UInt256,
|
||||||
minPrice: UInt256,
|
minPricePerBytePerSecond: UInt256,
|
||||||
maxCollateral: UInt256,
|
totalCollateral: UInt256,
|
||||||
): Future[?!Availability] {.async.} =
|
): Future[?!Availability] {.async.} =
|
||||||
trace "creating availability", size, duration, minPrice, maxCollateral
|
trace "creating availability",
|
||||||
|
size, duration, minPricePerBytePerSecond, totalCollateral
|
||||||
|
|
||||||
let availability = Availability.init(size, size, duration, minPrice, maxCollateral)
|
let availability =
|
||||||
|
Availability.init(size, size, duration, minPricePerBytePerSecond, totalCollateral)
|
||||||
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:
|
||||||
@ -400,6 +421,7 @@ method createReservation*(
|
|||||||
slotSize: UInt256,
|
slotSize: UInt256,
|
||||||
requestId: RequestId,
|
requestId: RequestId,
|
||||||
slotIndex: UInt256,
|
slotIndex: UInt256,
|
||||||
|
collateralPerByte: UInt256,
|
||||||
): Future[?!Reservation] {.async, base.} =
|
): Future[?!Reservation] {.async, base.} =
|
||||||
withLock(self.availabilityLock):
|
withLock(self.availabilityLock):
|
||||||
without availabilityKey =? availabilityId.key, error:
|
without availabilityKey =? availabilityId.key, error:
|
||||||
@ -412,7 +434,7 @@ method createReservation*(
|
|||||||
if availability.freeSize < slotSize:
|
if availability.freeSize < slotSize:
|
||||||
let error = newException(
|
let error = newException(
|
||||||
BytesOutOfBoundsError,
|
BytesOutOfBoundsError,
|
||||||
"trying to reserve an amount of bytes that is greater than the total size of the Availability",
|
"trying to reserve an amount of bytes that is greater than the free size of the Availability",
|
||||||
)
|
)
|
||||||
return failure(error)
|
return failure(error)
|
||||||
|
|
||||||
@ -427,6 +449,9 @@ method createReservation*(
|
|||||||
# the newly created Reservation
|
# the newly created Reservation
|
||||||
availability.freeSize -= slotSize
|
availability.freeSize -= slotSize
|
||||||
|
|
||||||
|
# adjust the remaining totalRemainingCollateral
|
||||||
|
availability.totalRemainingCollateral -= slotSize * collateralPerByte
|
||||||
|
|
||||||
# update availability with reduced size
|
# update availability with reduced size
|
||||||
trace "Updating availability with reduced size"
|
trace "Updating availability with reduced size"
|
||||||
if updateErr =? (await self.updateAvailability(availability)).errorOption:
|
if updateErr =? (await self.updateAvailability(availability)).errorOption:
|
||||||
@ -617,7 +642,8 @@ proc all*(
|
|||||||
return await self.allImpl(T, key)
|
return await self.allImpl(T, key)
|
||||||
|
|
||||||
proc findAvailability*(
|
proc findAvailability*(
|
||||||
self: Reservations, size, duration, minPrice, collateral: UInt256
|
self: Reservations,
|
||||||
|
size, duration, pricePerBytePerSecond, collateralPerByte: UInt256,
|
||||||
): Future[?Availability] {.async.} =
|
): Future[?Availability] {.async.} =
|
||||||
without storables =? (await self.storables(Availability)), e:
|
without storables =? (await self.storables(Availability)), e:
|
||||||
error "failed to get all storables", error = e.msg
|
error "failed to get all storables", error = e.msg
|
||||||
@ -626,17 +652,18 @@ 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 size <= availability.freeSize and duration <= availability.duration and
|
||||||
collateral <= availability.maxCollateral and minPrice >= availability.minPrice:
|
collateralPerByte <= availability.maxCollateralPerByte and
|
||||||
|
pricePerBytePerSecond >= availability.minPricePerBytePerSecond:
|
||||||
trace "availability matched",
|
trace "availability matched",
|
||||||
id = availability.id,
|
id = availability.id,
|
||||||
size,
|
size,
|
||||||
availFreeSize = availability.freeSize,
|
availFreeSize = availability.freeSize,
|
||||||
duration,
|
duration,
|
||||||
availDuration = availability.duration,
|
availDuration = availability.duration,
|
||||||
minPrice,
|
pricePerBytePerSecond,
|
||||||
availMinPrice = availability.minPrice,
|
availMinPricePerBytePerSecond = availability.minPricePerBytePerSecond,
|
||||||
collateral,
|
collateralPerByte,
|
||||||
availMaxCollateral = availability.maxCollateral
|
availMaxCollateralPerByte = availability.maxCollateralPerByte
|
||||||
|
|
||||||
# TODO: As soon as we're on ARC-ORC, we can use destructors
|
# TODO: As soon as we're on ARC-ORC, we can use destructors
|
||||||
# to automatically dispose our iterators when they fall out of scope.
|
# to automatically dispose our iterators when they fall out of scope.
|
||||||
@ -652,7 +679,7 @@ proc findAvailability*(
|
|||||||
availFreeSize = availability.freeSize,
|
availFreeSize = availability.freeSize,
|
||||||
duration,
|
duration,
|
||||||
availDuration = availability.duration,
|
availDuration = availability.duration,
|
||||||
minPrice,
|
pricePerBytePerSecond,
|
||||||
availMinPrice = availability.minPrice,
|
availMinPricePerBytePerSecond = availability.minPricePerBytePerSecond,
|
||||||
collateral,
|
collateralPerByte,
|
||||||
availMaxCollateral = availability.maxCollateral
|
availMaxCollateralPerByte = availability.maxCollateralPerByte
|
||||||
|
|||||||
@ -25,9 +25,9 @@ type
|
|||||||
onCleanUp*: OnCleanUp
|
onCleanUp*: OnCleanUp
|
||||||
onFilled*: ?OnFilled
|
onFilled*: ?OnFilled
|
||||||
|
|
||||||
OnCleanUp* = proc(returnBytes = false, reprocessSlot = false): Future[void] {.
|
OnCleanUp* = proc(
|
||||||
gcsafe, upraises: []
|
returnBytes = false, reprocessSlot = false, returnedCollateral = UInt256.none
|
||||||
.}
|
): Future[void] {.gcsafe, upraises: [].}
|
||||||
OnFilled* = proc(request: StorageRequest, slotIndex: UInt256) {.gcsafe, upraises: [].}
|
OnFilled* = proc(request: StorageRequest, slotIndex: UInt256) {.gcsafe, upraises: [].}
|
||||||
|
|
||||||
SalesAgentError = object of CodexError
|
SalesAgentError = object of CodexError
|
||||||
|
|||||||
@ -32,8 +32,8 @@ type
|
|||||||
slotIndex: uint16
|
slotIndex: uint16
|
||||||
slotSize: UInt256
|
slotSize: UInt256
|
||||||
duration: UInt256
|
duration: UInt256
|
||||||
reward: UInt256
|
pricePerBytePerSecond: UInt256
|
||||||
collateral: UInt256
|
collateralPerByte: UInt256
|
||||||
expiry: UInt256
|
expiry: UInt256
|
||||||
seen: bool
|
seen: bool
|
||||||
|
|
||||||
@ -70,12 +70,14 @@ const DefaultMaxSize = 128'u16
|
|||||||
|
|
||||||
proc profitability(item: SlotQueueItem): UInt256 =
|
proc profitability(item: SlotQueueItem): UInt256 =
|
||||||
StorageAsk(
|
StorageAsk(
|
||||||
collateral: item.collateral,
|
|
||||||
duration: item.duration,
|
duration: item.duration,
|
||||||
reward: item.reward,
|
pricePerBytePerSecond: item.pricePerBytePerSecond,
|
||||||
slotSize: item.slotSize,
|
slotSize: item.slotSize,
|
||||||
).pricePerSlot
|
).pricePerSlot
|
||||||
|
|
||||||
|
proc collateralPerSlot(item: SlotQueueItem): UInt256 =
|
||||||
|
StorageAsk(collateralPerByte: item.collateralPerByte, slotSize: item.slotSize).collateralPerSlot
|
||||||
|
|
||||||
proc `<`*(a, b: SlotQueueItem): bool =
|
proc `<`*(a, b: SlotQueueItem): bool =
|
||||||
# for A to have a higher priority than B (in a min queue), A must be less than
|
# for A to have a higher priority than B (in a min queue), A must be less than
|
||||||
# B.
|
# B.
|
||||||
@ -92,15 +94,12 @@ proc `<`*(a, b: SlotQueueItem): bool =
|
|||||||
scoreA.addIf(a.profitability > b.profitability, 3)
|
scoreA.addIf(a.profitability > b.profitability, 3)
|
||||||
scoreB.addIf(a.profitability < b.profitability, 3)
|
scoreB.addIf(a.profitability < b.profitability, 3)
|
||||||
|
|
||||||
scoreA.addIf(a.collateral < b.collateral, 2)
|
scoreA.addIf(a.collateralPerSlot < b.collateralPerSlot, 2)
|
||||||
scoreB.addIf(a.collateral > b.collateral, 2)
|
scoreB.addIf(a.collateralPerSlot > b.collateralPerSlot, 2)
|
||||||
|
|
||||||
scoreA.addIf(a.expiry > b.expiry, 1)
|
scoreA.addIf(a.expiry > b.expiry, 1)
|
||||||
scoreB.addIf(a.expiry < b.expiry, 1)
|
scoreB.addIf(a.expiry < b.expiry, 1)
|
||||||
|
|
||||||
scoreA.addIf(a.slotSize < b.slotSize, 0)
|
|
||||||
scoreB.addIf(a.slotSize > b.slotSize, 0)
|
|
||||||
|
|
||||||
return scoreA > scoreB
|
return scoreA > scoreB
|
||||||
|
|
||||||
proc `==`*(a, b: SlotQueueItem): bool =
|
proc `==`*(a, b: SlotQueueItem): bool =
|
||||||
@ -144,8 +143,8 @@ proc init*(
|
|||||||
slotIndex: slotIndex,
|
slotIndex: slotIndex,
|
||||||
slotSize: ask.slotSize,
|
slotSize: ask.slotSize,
|
||||||
duration: ask.duration,
|
duration: ask.duration,
|
||||||
reward: ask.reward,
|
pricePerBytePerSecond: ask.pricePerBytePerSecond,
|
||||||
collateral: ask.collateral,
|
collateralPerByte: ask.collateralPerByte,
|
||||||
expiry: expiry,
|
expiry: expiry,
|
||||||
seen: seen,
|
seen: seen,
|
||||||
)
|
)
|
||||||
@ -189,11 +188,11 @@ proc slotSize*(self: SlotQueueItem): UInt256 =
|
|||||||
proc duration*(self: SlotQueueItem): UInt256 =
|
proc duration*(self: SlotQueueItem): UInt256 =
|
||||||
self.duration
|
self.duration
|
||||||
|
|
||||||
proc reward*(self: SlotQueueItem): UInt256 =
|
proc pricePerBytePerSecond*(self: SlotQueueItem): UInt256 =
|
||||||
self.reward
|
self.pricePerBytePerSecond
|
||||||
|
|
||||||
proc collateral*(self: SlotQueueItem): UInt256 =
|
proc collateralPerByte*(self: SlotQueueItem): UInt256 =
|
||||||
self.collateral
|
self.collateralPerByte
|
||||||
|
|
||||||
proc seen*(self: SlotQueueItem): bool =
|
proc seen*(self: SlotQueueItem): bool =
|
||||||
self.seen
|
self.seen
|
||||||
@ -246,8 +245,8 @@ proc populateItem*(
|
|||||||
slotIndex: slotIndex,
|
slotIndex: slotIndex,
|
||||||
slotSize: item.slotSize,
|
slotSize: item.slotSize,
|
||||||
duration: item.duration,
|
duration: item.duration,
|
||||||
reward: item.reward,
|
pricePerBytePerSecond: item.pricePerBytePerSecond,
|
||||||
collateral: item.collateral,
|
collateralPerByte: item.collateralPerByte,
|
||||||
expiry: item.expiry,
|
expiry: item.expiry,
|
||||||
)
|
)
|
||||||
return none SlotQueueItem
|
return none SlotQueueItem
|
||||||
|
|||||||
@ -22,13 +22,18 @@ method run*(state: SaleCancelled, machine: Machine): Future[?State] {.async.} =
|
|||||||
let slot = Slot(request: request, slotIndex: data.slotIndex)
|
let slot = Slot(request: request, slotIndex: data.slotIndex)
|
||||||
debug "Collecting collateral and partial payout",
|
debug "Collecting collateral and partial payout",
|
||||||
requestId = data.requestId, slotIndex = data.slotIndex
|
requestId = data.requestId, slotIndex = data.slotIndex
|
||||||
|
let currentCollateral = await market.currentCollateral(slot.id)
|
||||||
await market.freeSlot(slot.id)
|
await market.freeSlot(slot.id)
|
||||||
|
|
||||||
if onClear =? agent.context.onClear and request =? data.request:
|
if onClear =? agent.context.onClear and request =? data.request:
|
||||||
onClear(request, data.slotIndex)
|
onClear(request, data.slotIndex)
|
||||||
|
|
||||||
if onCleanUp =? agent.onCleanUp:
|
if onCleanUp =? agent.onCleanUp:
|
||||||
await onCleanUp(returnBytes = true, reprocessSlot = false)
|
await onCleanUp(
|
||||||
|
returnBytes = true,
|
||||||
|
reprocessSlot = false,
|
||||||
|
returnedCollateral = some currentCollateral,
|
||||||
|
)
|
||||||
|
|
||||||
warn "Sale cancelled due to timeout",
|
warn "Sale cancelled due to timeout",
|
||||||
requestId = data.requestId, slotIndex = data.slotIndex
|
requestId = data.requestId, slotIndex = data.slotIndex
|
||||||
|
|||||||
@ -28,7 +28,7 @@ method onFailed*(state: SaleFilling, request: StorageRequest): ?State =
|
|||||||
method run(state: SaleFilling, machine: Machine): Future[?State] {.async.} =
|
method run(state: SaleFilling, machine: Machine): Future[?State] {.async.} =
|
||||||
let data = SalesAgent(machine).data
|
let data = SalesAgent(machine).data
|
||||||
let market = SalesAgent(machine).context.market
|
let market = SalesAgent(machine).context.market
|
||||||
without (fullCollateral =? data.request .? ask .? collateral):
|
without (request =? data.request):
|
||||||
raiseAssert "Request not set"
|
raiseAssert "Request not set"
|
||||||
|
|
||||||
logScope:
|
logScope:
|
||||||
@ -36,15 +36,17 @@ method run(state: SaleFilling, machine: Machine): Future[?State] {.async.} =
|
|||||||
slotIndex = data.slotIndex
|
slotIndex = data.slotIndex
|
||||||
|
|
||||||
let slotState = await market.slotState(slotId(data.requestId, data.slotIndex))
|
let slotState = await market.slotState(slotId(data.requestId, data.slotIndex))
|
||||||
|
let requestedCollateral = request.ask.collateralPerSlot
|
||||||
var collateral: UInt256
|
var collateral: UInt256
|
||||||
|
|
||||||
if slotState == SlotState.Repair:
|
if slotState == SlotState.Repair:
|
||||||
# When repairing the node gets "discount" on the collateral that it needs to
|
# When repairing the node gets "discount" on the collateral that it needs to
|
||||||
let repairRewardPercentage = (await market.repairRewardPercentage).u256
|
let repairRewardPercentage = (await market.repairRewardPercentage).u256
|
||||||
collateral =
|
collateral =
|
||||||
fullCollateral - ((fullCollateral * repairRewardPercentage)).div(100.u256)
|
requestedCollateral -
|
||||||
|
((requestedCollateral * repairRewardPercentage)).div(100.u256)
|
||||||
else:
|
else:
|
||||||
collateral = fullCollateral
|
collateral = requestedCollateral
|
||||||
|
|
||||||
debug "Filling slot"
|
debug "Filling slot"
|
||||||
try:
|
try:
|
||||||
|
|||||||
@ -11,6 +11,7 @@ logScope:
|
|||||||
topics = "marketplace sales finished"
|
topics = "marketplace sales finished"
|
||||||
|
|
||||||
type SaleFinished* = ref object of ErrorHandlingState
|
type SaleFinished* = ref object of ErrorHandlingState
|
||||||
|
returnedCollateral*: ?UInt256
|
||||||
|
|
||||||
method `$`*(state: SaleFinished): string =
|
method `$`*(state: SaleFinished): string =
|
||||||
"SaleFinished"
|
"SaleFinished"
|
||||||
@ -32,4 +33,4 @@ method run*(state: SaleFinished, machine: Machine): Future[?State] {.async.} =
|
|||||||
requestId = data.requestId, slotIndex = data.slotIndex
|
requestId = data.requestId, slotIndex = data.slotIndex
|
||||||
|
|
||||||
if onCleanUp =? agent.onCleanUp:
|
if onCleanUp =? agent.onCleanUp:
|
||||||
await onCleanUp()
|
await onCleanUp(returnedCollateral = state.returnedCollateral)
|
||||||
|
|||||||
@ -21,7 +21,7 @@ method onCancelled*(state: SalePayout, request: StorageRequest): ?State =
|
|||||||
method onFailed*(state: SalePayout, request: StorageRequest): ?State =
|
method onFailed*(state: SalePayout, request: StorageRequest): ?State =
|
||||||
return some State(SaleFailed())
|
return some State(SaleFailed())
|
||||||
|
|
||||||
method run(state: SalePayout, machine: Machine): Future[?State] {.async.} =
|
method run*(state: SalePayout, machine: Machine): Future[?State] {.async.} =
|
||||||
let data = SalesAgent(machine).data
|
let data = SalesAgent(machine).data
|
||||||
let market = SalesAgent(machine).context.market
|
let market = SalesAgent(machine).context.market
|
||||||
|
|
||||||
@ -31,6 +31,7 @@ method run(state: SalePayout, machine: Machine): Future[?State] {.async.} =
|
|||||||
let slot = Slot(request: request, slotIndex: data.slotIndex)
|
let slot = Slot(request: request, slotIndex: data.slotIndex)
|
||||||
debug "Collecting finished slot's reward",
|
debug "Collecting finished slot's reward",
|
||||||
requestId = data.requestId, slotIndex = data.slotIndex
|
requestId = data.requestId, slotIndex = data.slotIndex
|
||||||
|
let currentCollateral = await market.currentCollateral(slot.id)
|
||||||
await market.freeSlot(slot.id)
|
await market.freeSlot(slot.id)
|
||||||
|
|
||||||
return some State(SaleFinished())
|
return some State(SaleFinished(returnedCollateral: some currentCollateral))
|
||||||
|
|||||||
@ -62,15 +62,13 @@ method run*(state: SalePreparing, machine: Machine): Future[?State] {.async.} =
|
|||||||
slotIndex = data.slotIndex
|
slotIndex = data.slotIndex
|
||||||
slotSize = request.ask.slotSize
|
slotSize = request.ask.slotSize
|
||||||
duration = request.ask.duration
|
duration = request.ask.duration
|
||||||
pricePerSlot = request.ask.pricePerSlot
|
pricePerBytePerSecond = request.ask.pricePerBytePerSecond
|
||||||
|
collateralPerByte = request.ask.collateralPerByte
|
||||||
|
|
||||||
# availability was checked for this slot when it entered the queue, however
|
|
||||||
# check to the ensure that there is still availability as they may have
|
|
||||||
# changed since being added (other slots may have been processed in that time)
|
|
||||||
without availability =?
|
without availability =?
|
||||||
await reservations.findAvailability(
|
await reservations.findAvailability(
|
||||||
request.ask.slotSize, request.ask.duration, request.ask.pricePerSlot,
|
request.ask.slotSize, request.ask.duration, request.ask.pricePerBytePerSecond,
|
||||||
request.ask.collateral,
|
request.ask.collateralPerByte,
|
||||||
):
|
):
|
||||||
debug "No availability found for request, ignoring"
|
debug "No availability found for request, ignoring"
|
||||||
|
|
||||||
@ -80,7 +78,8 @@ method run*(state: SalePreparing, machine: Machine): Future[?State] {.async.} =
|
|||||||
|
|
||||||
without reservation =?
|
without reservation =?
|
||||||
await reservations.createReservation(
|
await reservations.createReservation(
|
||||||
availability.id, request.ask.slotSize, request.id, data.slotIndex
|
availability.id, request.ask.slotSize, request.id, data.slotIndex,
|
||||||
|
request.ask.collateralPerByte,
|
||||||
), error:
|
), error:
|
||||||
trace "Creation of reservation failed"
|
trace "Creation of reservation failed"
|
||||||
# Race condition:
|
# Race condition:
|
||||||
|
|||||||
90
openapi.yaml
90
openapi.yaml
@ -6,7 +6,7 @@ info:
|
|||||||
description: "List of endpoints and interfaces available to Codex API users"
|
description: "List of endpoints and interfaces available to Codex API users"
|
||||||
|
|
||||||
security:
|
security:
|
||||||
- { }
|
- {}
|
||||||
|
|
||||||
components:
|
components:
|
||||||
schemas:
|
schemas:
|
||||||
@ -50,9 +50,9 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
description: Address of Ethereum address
|
description: Address of Ethereum address
|
||||||
|
|
||||||
Reward:
|
PricePerBytePerSecond:
|
||||||
type: string
|
type: string
|
||||||
description: The maximum amount of tokens paid per second per slot to hosts the client is willing to pay
|
description: The amount of tokens paid per byte per second per slot to hosts the client is willing to pay
|
||||||
|
|
||||||
Duration:
|
Duration:
|
||||||
type: string
|
type: string
|
||||||
@ -157,12 +157,12 @@ components:
|
|||||||
description: Total size of availability's storage in bytes as decimal string
|
description: Total size of availability's storage in bytes as decimal string
|
||||||
duration:
|
duration:
|
||||||
$ref: "#/components/schemas/Duration"
|
$ref: "#/components/schemas/Duration"
|
||||||
minPrice:
|
minPricePerBytePerSecond:
|
||||||
type: string
|
type: string
|
||||||
description: Minimal price paid (in amount of tokens) for the whole hosted request's slot for the request's duration as decimal string
|
description: Minimal price per byte per second paid (in amount of tokens) for the hosted request's slot for the request's duration as decimal string
|
||||||
maxCollateral:
|
totalCollateral:
|
||||||
type: string
|
type: string
|
||||||
description: Maximum collateral user is willing to pay per filled Slot (in amount of tokens) as decimal string
|
description: Total collateral (in amount of tokens) that can be used for matching requests
|
||||||
|
|
||||||
SalesAvailabilityREAD:
|
SalesAvailabilityREAD:
|
||||||
allOf:
|
allOf:
|
||||||
@ -178,8 +178,8 @@ components:
|
|||||||
- $ref: "#/components/schemas/SalesAvailability"
|
- $ref: "#/components/schemas/SalesAvailability"
|
||||||
- required:
|
- required:
|
||||||
- totalSize
|
- totalSize
|
||||||
- minPrice
|
- minPricePerBytePerSecond
|
||||||
- maxCollateral
|
- totalCollateral
|
||||||
- duration
|
- duration
|
||||||
|
|
||||||
Slot:
|
Slot:
|
||||||
@ -243,16 +243,16 @@ components:
|
|||||||
StorageRequestCreation:
|
StorageRequestCreation:
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
- reward
|
- pricePerBytePerSecond
|
||||||
- duration
|
- duration
|
||||||
- proofProbability
|
- proofProbability
|
||||||
- collateral
|
- collateralPerByte
|
||||||
- expiry
|
- expiry
|
||||||
properties:
|
properties:
|
||||||
duration:
|
duration:
|
||||||
$ref: "#/components/schemas/Duration"
|
$ref: "#/components/schemas/Duration"
|
||||||
reward:
|
pricePerBytePerSecond:
|
||||||
$ref: "#/components/schemas/Reward"
|
$ref: "#/components/schemas/PricePerBytePerSecond"
|
||||||
proofProbability:
|
proofProbability:
|
||||||
$ref: "#/components/schemas/ProofProbability"
|
$ref: "#/components/schemas/ProofProbability"
|
||||||
nodes:
|
nodes:
|
||||||
@ -263,16 +263,16 @@ components:
|
|||||||
description: Additional number of nodes on top of the `nodes` property that can be lost before pronouncing the content lost
|
description: Additional number of nodes on top of the `nodes` property that can be lost before pronouncing the content lost
|
||||||
type: integer
|
type: integer
|
||||||
default: 0
|
default: 0
|
||||||
collateral:
|
collateralPerByte:
|
||||||
type: string
|
type: string
|
||||||
description: Number as decimal string that represents how much collateral is asked from hosts that wants to fill a slots
|
description: Number as decimal string that represents how much collateral per byte is asked from hosts that wants to fill a slots
|
||||||
expiry:
|
expiry:
|
||||||
type: string
|
type: string
|
||||||
description: Number as decimal string that represents expiry threshold in seconds from when the Request is submitted. When the threshold is reached and the Request does not find requested amount of nodes to host the data, the Request is voided. The number of seconds can not be higher then the Request's duration itself.
|
description: Number as decimal string that represents expiry threshold in seconds from when the Request is submitted. When the threshold is reached and the Request does not find requested amount of nodes to host the data, the Request is voided. The number of seconds can not be higher then the Request's duration itself.
|
||||||
StorageAsk:
|
StorageAsk:
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
- reward
|
- pricePerBytePerSecond
|
||||||
properties:
|
properties:
|
||||||
slots:
|
slots:
|
||||||
description: Number of slots (eq. hosts) that the Request want to have the content spread over
|
description: Number of slots (eq. hosts) that the Request want to have the content spread over
|
||||||
@ -284,8 +284,8 @@ components:
|
|||||||
$ref: "#/components/schemas/Duration"
|
$ref: "#/components/schemas/Duration"
|
||||||
proofProbability:
|
proofProbability:
|
||||||
$ref: "#/components/schemas/ProofProbability"
|
$ref: "#/components/schemas/ProofProbability"
|
||||||
reward:
|
pricePerBytePerSecond:
|
||||||
$ref: "#/components/schemas/Reward"
|
$ref: "#/components/schemas/PricePerBytePerSecond"
|
||||||
maxSlotLoss:
|
maxSlotLoss:
|
||||||
type: integer
|
type: integer
|
||||||
description: Max slots that can be lost without data considered to be lost
|
description: Max slots that can be lost without data considered to be lost
|
||||||
@ -418,14 +418,14 @@ paths:
|
|||||||
description: |
|
description: |
|
||||||
If `addrs` param is supplied, it will be used to dial the peer, otherwise the `peerId` is used
|
If `addrs` param is supplied, it will be used to dial the peer, otherwise the `peerId` is used
|
||||||
to invoke peer discovery, if it succeeds the returned addresses will be used to dial.
|
to invoke peer discovery, if it succeeds the returned addresses will be used to dial.
|
||||||
tags: [ Node ]
|
tags: [Node]
|
||||||
operationId: connectPeer
|
operationId: connectPeer
|
||||||
parameters:
|
parameters:
|
||||||
- in: path
|
- in: path
|
||||||
name: peerId
|
name: peerId
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/PeerId"
|
$ref: "#/components/schemas/PeerId"
|
||||||
description: Peer that should be dialed.
|
description: Peer that should be dialed.
|
||||||
- in: query
|
- in: query
|
||||||
name: addrs
|
name: addrs
|
||||||
@ -448,7 +448,7 @@ paths:
|
|||||||
"/data":
|
"/data":
|
||||||
get:
|
get:
|
||||||
summary: "Lists manifest CIDs stored locally in node."
|
summary: "Lists manifest CIDs stored locally in node."
|
||||||
tags: [ Data ]
|
tags: [Data]
|
||||||
operationId: listData
|
operationId: listData
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
@ -468,7 +468,7 @@ paths:
|
|||||||
description: Well it was bad-bad
|
description: Well it was bad-bad
|
||||||
post:
|
post:
|
||||||
summary: "Upload a file in a streaming manner. Once finished, the file is stored in the node and can be retrieved by any node in the network using the returned CID."
|
summary: "Upload a file in a streaming manner. Once finished, the file is stored in the node and can be retrieved by any node in the network using the returned CID."
|
||||||
tags: [ Data ]
|
tags: [Data]
|
||||||
operationId: upload
|
operationId: upload
|
||||||
parameters:
|
parameters:
|
||||||
- name: content-type
|
- name: content-type
|
||||||
@ -484,7 +484,7 @@ paths:
|
|||||||
description: The content disposition used to send the filename.
|
description: The content disposition used to send the filename.
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
example: "attachment; filename=\"codex.png\""
|
example: 'attachment; filename="codex.png"'
|
||||||
requestBody:
|
requestBody:
|
||||||
content:
|
content:
|
||||||
application/octet-stream:
|
application/octet-stream:
|
||||||
@ -504,14 +504,14 @@ paths:
|
|||||||
"/data/{cid}":
|
"/data/{cid}":
|
||||||
get:
|
get:
|
||||||
summary: "Download a file from the local node in a streaming manner. If the file is not available locally, a 404 is returned."
|
summary: "Download a file from the local node in a streaming manner. If the file is not available locally, a 404 is returned."
|
||||||
tags: [ Data ]
|
tags: [Data]
|
||||||
operationId: downloadLocal
|
operationId: downloadLocal
|
||||||
parameters:
|
parameters:
|
||||||
- in: path
|
- in: path
|
||||||
name: cid
|
name: cid
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/Cid"
|
$ref: "#/components/schemas/Cid"
|
||||||
description: File to be downloaded.
|
description: File to be downloaded.
|
||||||
|
|
||||||
responses:
|
responses:
|
||||||
@ -532,14 +532,14 @@ paths:
|
|||||||
"/data/{cid}/network":
|
"/data/{cid}/network":
|
||||||
post:
|
post:
|
||||||
summary: "Download a file from the network to the local node if it's not available locally. Note: Download is performed async. Call can return before download is completed."
|
summary: "Download a file from the network to the local node if it's not available locally. Note: Download is performed async. Call can return before download is completed."
|
||||||
tags: [ Data ]
|
tags: [Data]
|
||||||
operationId: downloadNetwork
|
operationId: downloadNetwork
|
||||||
parameters:
|
parameters:
|
||||||
- in: path
|
- in: path
|
||||||
name: cid
|
name: cid
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/Cid"
|
$ref: "#/components/schemas/Cid"
|
||||||
description: "File to be downloaded."
|
description: "File to be downloaded."
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
@ -558,14 +558,14 @@ paths:
|
|||||||
"/data/{cid}/network/stream":
|
"/data/{cid}/network/stream":
|
||||||
get:
|
get:
|
||||||
summary: "Download a file from the network in a streaming manner. If the file is not available locally, it will be retrieved from other nodes in the network if able."
|
summary: "Download a file from the network in a streaming manner. If the file is not available locally, it will be retrieved from other nodes in the network if able."
|
||||||
tags: [ Data ]
|
tags: [Data]
|
||||||
operationId: downloadNetworkStream
|
operationId: downloadNetworkStream
|
||||||
parameters:
|
parameters:
|
||||||
- in: path
|
- in: path
|
||||||
name: cid
|
name: cid
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/Cid"
|
$ref: "#/components/schemas/Cid"
|
||||||
description: "File to be downloaded."
|
description: "File to be downloaded."
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
@ -585,14 +585,14 @@ paths:
|
|||||||
"/data/{cid}/network/manifest":
|
"/data/{cid}/network/manifest":
|
||||||
get:
|
get:
|
||||||
summary: "Download only the dataset manifest from the network to the local node if it's not available locally."
|
summary: "Download only the dataset manifest from the network to the local node if it's not available locally."
|
||||||
tags: [ Data ]
|
tags: [Data]
|
||||||
operationId: downloadNetworkManifest
|
operationId: downloadNetworkManifest
|
||||||
parameters:
|
parameters:
|
||||||
- in: path
|
- in: path
|
||||||
name: cid
|
name: cid
|
||||||
required: true
|
required: true
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/Cid"
|
$ref: "#/components/schemas/Cid"
|
||||||
description: "File for which the manifest is to be downloaded."
|
description: "File for which the manifest is to be downloaded."
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
@ -611,7 +611,7 @@ paths:
|
|||||||
"/space":
|
"/space":
|
||||||
get:
|
get:
|
||||||
summary: "Gets a summary of the storage space allocation of the node."
|
summary: "Gets a summary of the storage space allocation of the node."
|
||||||
tags: [ Data ]
|
tags: [Data]
|
||||||
operationId: space
|
operationId: space
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
@ -627,7 +627,7 @@ paths:
|
|||||||
"/sales/slots":
|
"/sales/slots":
|
||||||
get:
|
get:
|
||||||
summary: "Returns active slots"
|
summary: "Returns active slots"
|
||||||
tags: [ Marketplace ]
|
tags: [Marketplace]
|
||||||
operationId: getActiveSlots
|
operationId: getActiveSlots
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
@ -645,7 +645,7 @@ paths:
|
|||||||
"/sales/slots/{slotId}":
|
"/sales/slots/{slotId}":
|
||||||
get:
|
get:
|
||||||
summary: "Returns active slot with id {slotId} for the host"
|
summary: "Returns active slot with id {slotId} for the host"
|
||||||
tags: [ Marketplace ]
|
tags: [Marketplace]
|
||||||
operationId: getActiveSlotById
|
operationId: getActiveSlotById
|
||||||
parameters:
|
parameters:
|
||||||
- in: path
|
- in: path
|
||||||
@ -674,7 +674,7 @@ paths:
|
|||||||
"/sales/availability":
|
"/sales/availability":
|
||||||
get:
|
get:
|
||||||
summary: "Returns storage that is for sale"
|
summary: "Returns storage that is for sale"
|
||||||
tags: [ Marketplace ]
|
tags: [Marketplace]
|
||||||
operationId: getAvailabilities
|
operationId: getAvailabilities
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
@ -693,7 +693,7 @@ paths:
|
|||||||
post:
|
post:
|
||||||
summary: "Offers storage for sale"
|
summary: "Offers storage for sale"
|
||||||
operationId: offerStorage
|
operationId: offerStorage
|
||||||
tags: [ Marketplace ]
|
tags: [Marketplace]
|
||||||
requestBody:
|
requestBody:
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
@ -721,7 +721,7 @@ paths:
|
|||||||
The new parameters will be only considered for new requests.
|
The new parameters will be only considered for new requests.
|
||||||
Existing Requests linked to this Availability will continue as is.
|
Existing Requests linked to this Availability will continue as is.
|
||||||
operationId: updateOfferedStorage
|
operationId: updateOfferedStorage
|
||||||
tags: [ Marketplace ]
|
tags: [Marketplace]
|
||||||
parameters:
|
parameters:
|
||||||
- in: path
|
- in: path
|
||||||
name: id
|
name: id
|
||||||
@ -753,7 +753,7 @@ paths:
|
|||||||
summary: "Get availability's reservations"
|
summary: "Get availability's reservations"
|
||||||
description: Return's list of Reservations for ongoing Storage Requests that the node hosts.
|
description: Return's list of Reservations for ongoing Storage Requests that the node hosts.
|
||||||
operationId: getReservations
|
operationId: getReservations
|
||||||
tags: [ Marketplace ]
|
tags: [Marketplace]
|
||||||
parameters:
|
parameters:
|
||||||
- in: path
|
- in: path
|
||||||
name: id
|
name: id
|
||||||
@ -782,7 +782,7 @@ paths:
|
|||||||
"/storage/request/{cid}":
|
"/storage/request/{cid}":
|
||||||
post:
|
post:
|
||||||
summary: "Creates a new Request for storage"
|
summary: "Creates a new Request for storage"
|
||||||
tags: [ Marketplace ]
|
tags: [Marketplace]
|
||||||
operationId: createStorageRequest
|
operationId: createStorageRequest
|
||||||
parameters:
|
parameters:
|
||||||
- in: path
|
- in: path
|
||||||
@ -813,7 +813,7 @@ paths:
|
|||||||
"/storage/purchases":
|
"/storage/purchases":
|
||||||
get:
|
get:
|
||||||
summary: "Returns list of purchase IDs"
|
summary: "Returns list of purchase IDs"
|
||||||
tags: [ Marketplace ]
|
tags: [Marketplace]
|
||||||
operationId: getPurchases
|
operationId: getPurchases
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
@ -830,7 +830,7 @@ paths:
|
|||||||
"/storage/purchases/{id}":
|
"/storage/purchases/{id}":
|
||||||
get:
|
get:
|
||||||
summary: "Returns purchase details"
|
summary: "Returns purchase details"
|
||||||
tags: [ Marketplace ]
|
tags: [Marketplace]
|
||||||
operationId: getPurchase
|
operationId: getPurchase
|
||||||
parameters:
|
parameters:
|
||||||
- in: path
|
- in: path
|
||||||
@ -857,7 +857,7 @@ paths:
|
|||||||
get:
|
get:
|
||||||
summary: "Get Node's SPR"
|
summary: "Get Node's SPR"
|
||||||
operationId: getSPR
|
operationId: getSPR
|
||||||
tags: [ Node ]
|
tags: [Node]
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: Node's SPR
|
description: Node's SPR
|
||||||
@ -875,7 +875,7 @@ paths:
|
|||||||
get:
|
get:
|
||||||
summary: "Get Node's PeerID"
|
summary: "Get Node's PeerID"
|
||||||
operationId: getPeerId
|
operationId: getPeerId
|
||||||
tags: [ Node ]
|
tags: [Node]
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: Node's Peer ID
|
description: Node's Peer ID
|
||||||
@ -890,7 +890,7 @@ paths:
|
|||||||
"/debug/chronicles/loglevel":
|
"/debug/chronicles/loglevel":
|
||||||
post:
|
post:
|
||||||
summary: "Set log level at run time"
|
summary: "Set log level at run time"
|
||||||
tags: [ Debug ]
|
tags: [Debug]
|
||||||
operationId: setDebugLogLevel
|
operationId: setDebugLogLevel
|
||||||
|
|
||||||
parameters:
|
parameters:
|
||||||
@ -912,7 +912,7 @@ paths:
|
|||||||
get:
|
get:
|
||||||
summary: "Gets node information"
|
summary: "Gets node information"
|
||||||
operationId: getDebugInfo
|
operationId: getDebugInfo
|
||||||
tags: [ Debug ]
|
tags: [Debug]
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: Node's information
|
description: Node's information
|
||||||
|
|||||||
@ -55,13 +55,16 @@ proc example*(_: type MultiHash, mcodec = Sha256HashCodec): MultiHash =
|
|||||||
let bytes = newSeqWith(256, rand(uint8))
|
let bytes = newSeqWith(256, rand(uint8))
|
||||||
MultiHash.digest($mcodec, bytes).tryGet()
|
MultiHash.digest($mcodec, bytes).tryGet()
|
||||||
|
|
||||||
proc example*(_: type Availability): Availability =
|
proc example*(
|
||||||
|
_: type Availability, collateralPerByte = uint8.example.u256
|
||||||
|
): Availability =
|
||||||
|
let totalSize = uint16.example.u256
|
||||||
Availability.init(
|
Availability.init(
|
||||||
totalSize = uint16.example.u256,
|
totalSize = totalSize,
|
||||||
freeSize = uint16.example.u256,
|
freeSize = uint16.example.u256,
|
||||||
duration = uint16.example.u256,
|
duration = uint16.example.u256,
|
||||||
minPrice = uint64.example.u256,
|
minPricePerBytePerSecond = uint8.example.u256,
|
||||||
maxCollateral = uint16.example.u256,
|
totalCollateral = totalSize * collateralPerByte,
|
||||||
)
|
)
|
||||||
|
|
||||||
proc example*(_: type Reservation): Reservation =
|
proc example*(_: type Reservation): Reservation =
|
||||||
|
|||||||
@ -60,6 +60,7 @@ type
|
|||||||
slotIndex*: UInt256
|
slotIndex*: UInt256
|
||||||
proof*: Groth16Proof
|
proof*: Groth16Proof
|
||||||
timestamp: ?SecondsSince1970
|
timestamp: ?SecondsSince1970
|
||||||
|
collateral*: UInt256
|
||||||
|
|
||||||
Subscriptions = object
|
Subscriptions = object
|
||||||
onRequest: seq[RequestSubscription]
|
onRequest: seq[RequestSubscription]
|
||||||
@ -205,6 +206,14 @@ method getHost*(
|
|||||||
return some slot.host
|
return some slot.host
|
||||||
return none Address
|
return none Address
|
||||||
|
|
||||||
|
method currentCollateral*(
|
||||||
|
market: MockMarket, slotId: SlotId
|
||||||
|
): Future[UInt256] {.async.} =
|
||||||
|
for slot in market.filled:
|
||||||
|
if slotId == slotId(slot.requestId, slot.slotIndex):
|
||||||
|
return slot.collateral
|
||||||
|
return 0.u256
|
||||||
|
|
||||||
proc emitSlotFilled*(market: MockMarket, requestId: RequestId, slotIndex: UInt256) =
|
proc emitSlotFilled*(market: MockMarket, requestId: RequestId, slotIndex: UInt256) =
|
||||||
var subscriptions = market.subscriptions.onSlotFilled
|
var subscriptions = market.subscriptions.onSlotFilled
|
||||||
for subscription in subscriptions:
|
for subscription in subscriptions:
|
||||||
@ -251,6 +260,7 @@ proc fillSlot*(
|
|||||||
slotIndex: UInt256,
|
slotIndex: UInt256,
|
||||||
proof: Groth16Proof,
|
proof: Groth16Proof,
|
||||||
host: Address,
|
host: Address,
|
||||||
|
collateral = 0.u256,
|
||||||
) =
|
) =
|
||||||
let slot = MockSlot(
|
let slot = MockSlot(
|
||||||
requestId: requestId,
|
requestId: requestId,
|
||||||
@ -258,6 +268,7 @@ proc fillSlot*(
|
|||||||
proof: proof,
|
proof: proof,
|
||||||
host: host,
|
host: host,
|
||||||
timestamp: market.clock .? now,
|
timestamp: market.clock .? now,
|
||||||
|
collateral: collateral,
|
||||||
)
|
)
|
||||||
market.filled.add(slot)
|
market.filled.add(slot)
|
||||||
market.slotState[slotId(slot.requestId, slot.slotIndex)] = SlotState.Filled
|
market.slotState[slotId(slot.requestId, slot.slotIndex)] = SlotState.Filled
|
||||||
@ -270,7 +281,7 @@ method fillSlot*(
|
|||||||
proof: Groth16Proof,
|
proof: Groth16Proof,
|
||||||
collateral: UInt256,
|
collateral: UInt256,
|
||||||
) {.async.} =
|
) {.async.} =
|
||||||
market.fillSlot(requestId, slotIndex, proof, market.signer)
|
market.fillSlot(requestId, slotIndex, proof, market.signer, collateral)
|
||||||
|
|
||||||
method freeSlot*(market: MockMarket, slotId: SlotId) {.async.} =
|
method freeSlot*(market: MockMarket, slotId: SlotId) {.async.} =
|
||||||
market.freed.add(slotId)
|
market.freed.add(slotId)
|
||||||
|
|||||||
@ -27,6 +27,7 @@ method createReservation*(
|
|||||||
slotSize: UInt256,
|
slotSize: UInt256,
|
||||||
requestId: RequestId,
|
requestId: RequestId,
|
||||||
slotIndex: UInt256,
|
slotIndex: UInt256,
|
||||||
|
collateralPerByte: UInt256,
|
||||||
): Future[?!Reservation] {.async.} =
|
): Future[?!Reservation] {.async.} =
|
||||||
if self.createReservationThrowBytesOutOfBoundsError:
|
if self.createReservationThrowBytesOutOfBoundsError:
|
||||||
let error = newException(
|
let error = newException(
|
||||||
@ -38,5 +39,10 @@ method createReservation*(
|
|||||||
return failure(error)
|
return failure(error)
|
||||||
|
|
||||||
return await procCall createReservation(
|
return await procCall createReservation(
|
||||||
Reservations(self), availabilityId, slotSize, requestId, slotIndex
|
Reservations(self),
|
||||||
|
availabilityId,
|
||||||
|
slotSize,
|
||||||
|
requestId,
|
||||||
|
slotIndex,
|
||||||
|
collateralPerByte,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -6,8 +6,8 @@ type MockSlotQueueItem* = object
|
|||||||
slotIndex*: uint16
|
slotIndex*: uint16
|
||||||
slotSize*: UInt256
|
slotSize*: UInt256
|
||||||
duration*: UInt256
|
duration*: UInt256
|
||||||
reward*: UInt256
|
pricePerBytePerSecond*: UInt256
|
||||||
collateral*: UInt256
|
collateralPerByte*: UInt256
|
||||||
expiry*: UInt256
|
expiry*: UInt256
|
||||||
seen*: bool
|
seen*: bool
|
||||||
|
|
||||||
@ -18,8 +18,8 @@ proc toSlotQueueItem*(item: MockSlotQueueItem): SlotQueueItem =
|
|||||||
ask = StorageAsk(
|
ask = StorageAsk(
|
||||||
slotSize: item.slotSize,
|
slotSize: item.slotSize,
|
||||||
duration: item.duration,
|
duration: item.duration,
|
||||||
reward: item.reward,
|
pricePerBytePerSecond: item.pricePerBytePerSecond,
|
||||||
collateral: item.collateral,
|
collateralPerByte: item.collateralPerByte,
|
||||||
),
|
),
|
||||||
expiry = item.expiry,
|
expiry = item.expiry,
|
||||||
seen = item.seen,
|
seen = item.seen,
|
||||||
|
|||||||
@ -155,10 +155,10 @@ asyncchecksuite "Test Node - Basic":
|
|||||||
nodes = 5,
|
nodes = 5,
|
||||||
tolerance = 2,
|
tolerance = 2,
|
||||||
duration = 100.u256,
|
duration = 100.u256,
|
||||||
reward = 2.u256,
|
pricePerBytePerSecond = 1.u256,
|
||||||
proofProbability = 3.u256,
|
proofProbability = 3.u256,
|
||||||
expiry = 200.u256,
|
expiry = 200.u256,
|
||||||
collateral = 200.u256,
|
collateralPerByte = 1.u256,
|
||||||
)
|
)
|
||||||
).tryGet
|
).tryGet
|
||||||
|
|
||||||
|
|||||||
@ -15,25 +15,40 @@ import ../../helpers/mockclock
|
|||||||
asyncchecksuite "sales state 'cancelled'":
|
asyncchecksuite "sales state 'cancelled'":
|
||||||
let request = StorageRequest.example
|
let request = StorageRequest.example
|
||||||
let slotIndex = (request.ask.slots div 2).u256
|
let slotIndex = (request.ask.slots div 2).u256
|
||||||
let market = MockMarket.new()
|
|
||||||
let clock = MockClock.new()
|
let clock = MockClock.new()
|
||||||
|
|
||||||
|
let currentCollateral = UInt256.example
|
||||||
|
|
||||||
|
var market: MockMarket
|
||||||
var state: SaleCancelled
|
var state: SaleCancelled
|
||||||
var agent: SalesAgent
|
var agent: SalesAgent
|
||||||
var returnBytesWas = false
|
var returnBytesWas = bool.none
|
||||||
var reprocessSlotWas = false
|
var reprocessSlotWas = bool.none
|
||||||
|
var returnedCollateralValue = UInt256.none
|
||||||
|
|
||||||
setup:
|
setup:
|
||||||
let onCleanUp = proc(returnBytes = false, reprocessSlot = false) {.async.} =
|
market = MockMarket.new()
|
||||||
returnBytesWas = returnBytes
|
let onCleanUp = proc(
|
||||||
reprocessSlotWas = reprocessSlot
|
returnBytes = false, reprocessSlot = false, returnedCollateral = UInt256.none
|
||||||
|
) {.async.} =
|
||||||
|
returnBytesWas = some returnBytes
|
||||||
|
reprocessSlotWas = some reprocessSlot
|
||||||
|
returnedCollateralValue = returnedCollateral
|
||||||
|
|
||||||
let context = SalesContext(market: market, clock: clock)
|
let context = SalesContext(market: market, clock: clock)
|
||||||
agent = newSalesAgent(context, request.id, slotIndex, request.some)
|
agent = newSalesAgent(context, request.id, slotIndex, request.some)
|
||||||
agent.onCleanUp = onCleanUp
|
agent.onCleanUp = onCleanUp
|
||||||
state = SaleCancelled.new()
|
state = SaleCancelled.new()
|
||||||
|
|
||||||
test "calls onCleanUp with returnBytes = false and reprocessSlot = true":
|
test "calls onCleanUp with returnBytes = false, reprocessSlot = true, and returnedCollateral = currentCollateral":
|
||||||
|
market.fillSlot(
|
||||||
|
requestId = request.id,
|
||||||
|
slotIndex = slotIndex,
|
||||||
|
proof = Groth16Proof.default,
|
||||||
|
host = Address.example,
|
||||||
|
collateral = currentCollateral,
|
||||||
|
)
|
||||||
discard await state.run(agent)
|
discard await state.run(agent)
|
||||||
check eventually returnBytesWas == true
|
check eventually returnBytesWas == some true
|
||||||
check eventually reprocessSlotWas == false
|
check eventually reprocessSlotWas == some false
|
||||||
|
check eventually returnedCollateralValue == some currentCollateral
|
||||||
|
|||||||
@ -24,7 +24,9 @@ asyncchecksuite "sales state 'errored'":
|
|||||||
var reprocessSlotWas = false
|
var reprocessSlotWas = false
|
||||||
|
|
||||||
setup:
|
setup:
|
||||||
let onCleanUp = proc(returnBytes = false, reprocessSlot = false) {.async.} =
|
let onCleanUp = proc(
|
||||||
|
returnBytes = false, reprocessSlot = false, returnedCollateral = UInt256.none
|
||||||
|
) {.async.} =
|
||||||
returnBytesWas = returnBytes
|
returnBytesWas = returnBytes
|
||||||
reprocessSlotWas = reprocessSlot
|
reprocessSlotWas = reprocessSlot
|
||||||
|
|
||||||
|
|||||||
@ -1,18 +1,45 @@
|
|||||||
import std/unittest
|
|
||||||
import pkg/questionable
|
import pkg/questionable
|
||||||
import pkg/codex/contracts/requests
|
import pkg/codex/contracts/requests
|
||||||
import pkg/codex/sales/states/finished
|
import pkg/codex/sales/states/finished
|
||||||
import pkg/codex/sales/states/cancelled
|
import pkg/codex/sales/states/cancelled
|
||||||
import pkg/codex/sales/states/failed
|
import pkg/codex/sales/states/failed
|
||||||
|
import pkg/codex/sales/salesagent
|
||||||
|
import pkg/codex/sales/salescontext
|
||||||
|
import pkg/codex/market
|
||||||
|
|
||||||
|
import ../../../asynctest
|
||||||
import ../../examples
|
import ../../examples
|
||||||
import ../../helpers
|
import ../../helpers
|
||||||
|
import ../../helpers/mockmarket
|
||||||
|
import ../../helpers/mockclock
|
||||||
|
|
||||||
checksuite "sales state 'finished'":
|
asyncchecksuite "sales state 'finished'":
|
||||||
let request = StorageRequest.example
|
let request = StorageRequest.example
|
||||||
|
let slotIndex = (request.ask.slots div 2).u256
|
||||||
|
let clock = MockClock.new()
|
||||||
|
|
||||||
|
let currentCollateral = UInt256.example
|
||||||
|
|
||||||
|
var market: MockMarket
|
||||||
var state: SaleFinished
|
var state: SaleFinished
|
||||||
|
var agent: SalesAgent
|
||||||
|
var returnBytesWas = bool.none
|
||||||
|
var reprocessSlotWas = bool.none
|
||||||
|
var returnedCollateralValue = UInt256.none
|
||||||
|
|
||||||
setup:
|
setup:
|
||||||
state = SaleFinished.new()
|
market = MockMarket.new()
|
||||||
|
let onCleanUp = proc(
|
||||||
|
returnBytes = false, reprocessSlot = false, returnedCollateral = UInt256.none
|
||||||
|
) {.async.} =
|
||||||
|
returnBytesWas = some returnBytes
|
||||||
|
reprocessSlotWas = some reprocessSlot
|
||||||
|
returnedCollateralValue = returnedCollateral
|
||||||
|
|
||||||
|
let context = SalesContext(market: market, clock: clock)
|
||||||
|
agent = newSalesAgent(context, request.id, slotIndex, request.some)
|
||||||
|
agent.onCleanUp = onCleanUp
|
||||||
|
state = SaleFinished(returnedCollateral: some currentCollateral)
|
||||||
|
|
||||||
test "switches to cancelled state when request expires":
|
test "switches to cancelled state when request expires":
|
||||||
let next = state.onCancelled(request)
|
let next = state.onCancelled(request)
|
||||||
@ -21,3 +48,9 @@ checksuite "sales state 'finished'":
|
|||||||
test "switches to failed state when request fails":
|
test "switches to failed state when request fails":
|
||||||
let next = state.onFailed(request)
|
let next = state.onFailed(request)
|
||||||
check !next of SaleFailed
|
check !next of SaleFailed
|
||||||
|
|
||||||
|
test "calls onCleanUp with returnBytes = false, reprocessSlot = true, and returnedCollateral = currentCollateral":
|
||||||
|
discard await state.run(agent)
|
||||||
|
check eventually returnBytesWas == some false
|
||||||
|
check eventually reprocessSlotWas == some false
|
||||||
|
check eventually returnedCollateralValue == some currentCollateral
|
||||||
|
|||||||
@ -24,7 +24,9 @@ asyncchecksuite "sales state 'ignored'":
|
|||||||
var reprocessSlotWas = false
|
var reprocessSlotWas = false
|
||||||
|
|
||||||
setup:
|
setup:
|
||||||
let onCleanUp = proc(returnBytes = false, reprocessSlot = false) {.async.} =
|
let onCleanUp = proc(
|
||||||
|
returnBytes = false, reprocessSlot = false, returnedCollateral = UInt256.none
|
||||||
|
) {.async.} =
|
||||||
returnBytesWas = returnBytes
|
returnBytesWas = returnBytes
|
||||||
reprocessSlotWas = reprocessSlot
|
reprocessSlotWas = reprocessSlot
|
||||||
|
|
||||||
|
|||||||
44
tests/codex/sales/states/testpayout.nim
Normal file
44
tests/codex/sales/states/testpayout.nim
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import pkg/questionable
|
||||||
|
import pkg/chronos
|
||||||
|
import pkg/codex/contracts/requests
|
||||||
|
import pkg/codex/sales/states/payout
|
||||||
|
import pkg/codex/sales/states/finished
|
||||||
|
import pkg/codex/sales/salesagent
|
||||||
|
import pkg/codex/sales/salescontext
|
||||||
|
import pkg/codex/market
|
||||||
|
|
||||||
|
import ../../../asynctest
|
||||||
|
import ../../examples
|
||||||
|
import ../../helpers
|
||||||
|
import ../../helpers/mockmarket
|
||||||
|
import ../../helpers/mockclock
|
||||||
|
|
||||||
|
asyncchecksuite "sales state 'payout'":
|
||||||
|
let request = StorageRequest.example
|
||||||
|
let slotIndex = (request.ask.slots div 2).u256
|
||||||
|
let clock = MockClock.new()
|
||||||
|
|
||||||
|
let currentCollateral = UInt256.example
|
||||||
|
|
||||||
|
var market: MockMarket
|
||||||
|
var state: SalePayout
|
||||||
|
var agent: SalesAgent
|
||||||
|
|
||||||
|
setup:
|
||||||
|
market = MockMarket.new()
|
||||||
|
|
||||||
|
let context = SalesContext(market: market, clock: clock)
|
||||||
|
agent = newSalesAgent(context, request.id, slotIndex, request.some)
|
||||||
|
state = SalePayout.new()
|
||||||
|
|
||||||
|
test "switches to 'finished' state and provides returnedCollateral":
|
||||||
|
market.fillSlot(
|
||||||
|
requestId = request.id,
|
||||||
|
slotIndex = slotIndex,
|
||||||
|
proof = Groth16Proof.default,
|
||||||
|
host = Address.example,
|
||||||
|
collateral = currentCollateral,
|
||||||
|
)
|
||||||
|
let next = await state.run(agent)
|
||||||
|
check !next of SaleFinished
|
||||||
|
check SaleFinished(!next).returnedCollateral == some currentCollateral
|
||||||
@ -33,12 +33,12 @@ asyncchecksuite "sales state 'preparing'":
|
|||||||
var reservations: MockReservations
|
var reservations: MockReservations
|
||||||
|
|
||||||
setup:
|
setup:
|
||||||
availability = Availability(
|
availability = Availability.init(
|
||||||
totalSize: request.ask.slotSize + 100.u256,
|
totalSize = request.ask.slotSize + 100.u256,
|
||||||
freeSize: request.ask.slotSize + 100.u256,
|
freeSize = request.ask.slotSize + 100.u256,
|
||||||
duration: request.ask.duration + 60.u256,
|
duration = request.ask.duration + 60.u256,
|
||||||
minPrice: request.ask.pricePerSlot - 10.u256,
|
minPricePerBytePerSecond = request.ask.pricePerBytePerSecond,
|
||||||
maxCollateral: request.ask.collateral + 400.u256,
|
totalCollateral = request.ask.collateralPerSlot * request.ask.slots.u256,
|
||||||
)
|
)
|
||||||
let repoDs = SQLiteDatastore.new(Memory).tryGet()
|
let repoDs = SQLiteDatastore.new(Memory).tryGet()
|
||||||
let metaDs = SQLiteDatastore.new(Memory).tryGet()
|
let metaDs = SQLiteDatastore.new(Memory).tryGet()
|
||||||
@ -69,8 +69,8 @@ asyncchecksuite "sales state 'preparing'":
|
|||||||
|
|
||||||
proc createAvailability() {.async.} =
|
proc createAvailability() {.async.} =
|
||||||
let a = await reservations.createAvailability(
|
let a = await reservations.createAvailability(
|
||||||
availability.totalSize, availability.duration, availability.minPrice,
|
availability.totalSize, availability.duration,
|
||||||
availability.maxCollateral,
|
availability.minPricePerBytePerSecond, availability.totalCollateral,
|
||||||
)
|
)
|
||||||
availability = a.get
|
availability = a.get
|
||||||
|
|
||||||
|
|||||||
@ -22,6 +22,7 @@ asyncchecksuite "Reservations module":
|
|||||||
repoDs: Datastore
|
repoDs: Datastore
|
||||||
metaDs: Datastore
|
metaDs: Datastore
|
||||||
reservations: Reservations
|
reservations: Reservations
|
||||||
|
collateralPerByte: UInt256
|
||||||
let
|
let
|
||||||
repoTmp = TempLevelDb.new()
|
repoTmp = TempLevelDb.new()
|
||||||
metaTmp = TempLevelDb.new()
|
metaTmp = TempLevelDb.new()
|
||||||
@ -32,23 +33,25 @@ asyncchecksuite "Reservations module":
|
|||||||
metaDs = metaTmp.newDb()
|
metaDs = metaTmp.newDb()
|
||||||
repo = RepoStore.new(repoDs, metaDs)
|
repo = RepoStore.new(repoDs, metaDs)
|
||||||
reservations = Reservations.new(repo)
|
reservations = Reservations.new(repo)
|
||||||
|
collateralPerByte = uint8.example.u256
|
||||||
|
|
||||||
teardown:
|
teardown:
|
||||||
await repoTmp.destroyDb()
|
await repoTmp.destroyDb()
|
||||||
await metaTmp.destroyDb()
|
await metaTmp.destroyDb()
|
||||||
|
|
||||||
proc createAvailability(): Availability =
|
proc createAvailability(): Availability =
|
||||||
let example = Availability.example
|
let example = Availability.example(collateralPerByte)
|
||||||
let totalSize = rand(100000 .. 200000)
|
let totalSize = rand(100000 .. 200000).u256
|
||||||
|
let totalCollateral = totalSize * collateralPerByte
|
||||||
let availability = waitFor reservations.createAvailability(
|
let availability = waitFor reservations.createAvailability(
|
||||||
totalSize.u256, example.duration, example.minPrice, example.maxCollateral
|
totalSize, example.duration, example.minPricePerBytePerSecond, totalCollateral
|
||||||
)
|
)
|
||||||
return availability.get
|
return availability.get
|
||||||
|
|
||||||
proc createReservation(availability: Availability): Reservation =
|
proc createReservation(availability: Availability): Reservation =
|
||||||
let size = rand(1 ..< availability.freeSize.truncate(int))
|
let size = rand(1 ..< availability.freeSize.truncate(int))
|
||||||
let reservation = waitFor reservations.createReservation(
|
let reservation = waitFor reservations.createReservation(
|
||||||
availability.id, size.u256, RequestId.example, UInt256.example
|
availability.id, size.u256, RequestId.example, UInt256.example, 1.u256
|
||||||
)
|
)
|
||||||
return reservation.get
|
return reservation.get
|
||||||
|
|
||||||
@ -126,7 +129,7 @@ asyncchecksuite "Reservations module":
|
|||||||
test "cannot create reservation with non-existant availability":
|
test "cannot create reservation with non-existant availability":
|
||||||
let availability = Availability.example
|
let availability = Availability.example
|
||||||
let created = await reservations.createReservation(
|
let created = await reservations.createReservation(
|
||||||
availability.id, UInt256.example, RequestId.example, UInt256.example
|
availability.id, UInt256.example, RequestId.example, UInt256.example, 1.u256
|
||||||
)
|
)
|
||||||
check created.isErr
|
check created.isErr
|
||||||
check created.error of NotExistsError
|
check created.error of NotExistsError
|
||||||
@ -134,7 +137,11 @@ asyncchecksuite "Reservations module":
|
|||||||
test "cannot create reservation larger than availability size":
|
test "cannot create reservation larger than availability size":
|
||||||
let availability = createAvailability()
|
let availability = createAvailability()
|
||||||
let created = await reservations.createReservation(
|
let created = await reservations.createReservation(
|
||||||
availability.id, availability.totalSize + 1, RequestId.example, UInt256.example
|
availability.id,
|
||||||
|
availability.totalSize + 1,
|
||||||
|
RequestId.example,
|
||||||
|
UInt256.example,
|
||||||
|
UInt256.example,
|
||||||
)
|
)
|
||||||
check created.isErr
|
check created.isErr
|
||||||
check created.error of BytesOutOfBoundsError
|
check created.error of BytesOutOfBoundsError
|
||||||
@ -143,11 +150,16 @@ asyncchecksuite "Reservations module":
|
|||||||
proc concurrencyTest(): Future[void] {.async.} =
|
proc concurrencyTest(): Future[void] {.async.} =
|
||||||
let availability = createAvailability()
|
let availability = createAvailability()
|
||||||
let one = reservations.createReservation(
|
let one = reservations.createReservation(
|
||||||
availability.id, availability.totalSize - 1, RequestId.example, UInt256.example
|
availability.id,
|
||||||
|
availability.totalSize - 1,
|
||||||
|
RequestId.example,
|
||||||
|
UInt256.example,
|
||||||
|
UInt256.example,
|
||||||
)
|
)
|
||||||
|
|
||||||
let two = reservations.createReservation(
|
let two = reservations.createReservation(
|
||||||
availability.id, availability.totalSize, RequestId.example, UInt256.example
|
availability.id, availability.totalSize, RequestId.example, UInt256.example,
|
||||||
|
UInt256.example,
|
||||||
)
|
)
|
||||||
|
|
||||||
let oneResult = await one
|
let oneResult = await one
|
||||||
@ -304,8 +316,8 @@ asyncchecksuite "Reservations module":
|
|||||||
let availability = createAvailability()
|
let availability = createAvailability()
|
||||||
|
|
||||||
let found = await reservations.findAvailability(
|
let found = await reservations.findAvailability(
|
||||||
availability.freeSize, availability.duration, availability.minPrice,
|
availability.freeSize, availability.duration,
|
||||||
availability.maxCollateral,
|
availability.minPricePerBytePerSecond, collateralPerByte,
|
||||||
)
|
)
|
||||||
|
|
||||||
check found.isSome
|
check found.isSome
|
||||||
@ -317,23 +329,22 @@ asyncchecksuite "Reservations module":
|
|||||||
let found = await reservations.findAvailability(
|
let found = await reservations.findAvailability(
|
||||||
availability.freeSize + 1,
|
availability.freeSize + 1,
|
||||||
availability.duration,
|
availability.duration,
|
||||||
availability.minPrice,
|
availability.minPricePerBytePerSecond,
|
||||||
availability.maxCollateral,
|
collateralPerByte,
|
||||||
)
|
)
|
||||||
|
|
||||||
check found.isNone
|
check found.isNone
|
||||||
|
|
||||||
test "non-existant availability cannot be found":
|
test "non-existent availability cannot be found":
|
||||||
let availability = Availability.example
|
let availability = Availability.example
|
||||||
let found = (
|
let found = await reservations.findAvailability(
|
||||||
await reservations.findAvailability(
|
availability.freeSize, availability.duration,
|
||||||
availability.freeSize, availability.duration, availability.minPrice,
|
availability.minPricePerBytePerSecond, collateralPerByte,
|
||||||
availability.maxCollateral,
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
check found.isNone
|
check found.isNone
|
||||||
|
|
||||||
test "non-existant availability cannot be retrieved":
|
test "non-existent availability cannot be retrieved":
|
||||||
let key = AvailabilityId.example.key.get
|
let key = AvailabilityId.example.key.get
|
||||||
let got = await reservations.get(key, Availability)
|
let got = await reservations.get(key, Availability)
|
||||||
check got.error of NotExistsError
|
check got.error of NotExistsError
|
||||||
|
|||||||
@ -43,8 +43,8 @@ asyncchecksuite "Sales - start":
|
|||||||
slots: 4,
|
slots: 4,
|
||||||
slotSize: 100.u256,
|
slotSize: 100.u256,
|
||||||
duration: 60.u256,
|
duration: 60.u256,
|
||||||
reward: 10.u256,
|
pricePerBytePerSecond: 1.u256,
|
||||||
collateral: 200.u256,
|
collateralPerByte: 1.u256,
|
||||||
),
|
),
|
||||||
content: StorageContent(cid: "some cid"),
|
content: StorageContent(cid: "some cid"),
|
||||||
expiry: (getTime() + initDuration(hours = 1)).toUnix.u256,
|
expiry: (getTime() + initDuration(hours = 1)).toUnix.u256,
|
||||||
@ -124,6 +124,10 @@ asyncchecksuite "Sales":
|
|||||||
repoTmp = TempLevelDb.new()
|
repoTmp = TempLevelDb.new()
|
||||||
metaTmp = TempLevelDb.new()
|
metaTmp = TempLevelDb.new()
|
||||||
|
|
||||||
|
var totalAvailabilitySize: UInt256
|
||||||
|
var minPricePerBytePerSecond: UInt256
|
||||||
|
var requestedCollateralPerByte: UInt256
|
||||||
|
var totalCollateral: UInt256
|
||||||
var availability: Availability
|
var availability: Availability
|
||||||
var request: StorageRequest
|
var request: StorageRequest
|
||||||
var sales: Sales
|
var sales: Sales
|
||||||
@ -135,20 +139,24 @@ asyncchecksuite "Sales":
|
|||||||
var itemsProcessed: seq[SlotQueueItem]
|
var itemsProcessed: seq[SlotQueueItem]
|
||||||
|
|
||||||
setup:
|
setup:
|
||||||
availability = Availability(
|
totalAvailabilitySize = 100.u256
|
||||||
totalSize: 100.u256,
|
minPricePerBytePerSecond = 1.u256
|
||||||
freeSize: 100.u256,
|
requestedCollateralPerByte = 1.u256
|
||||||
duration: 60.u256,
|
totalCollateral = requestedCollateralPerByte * totalAvailabilitySize
|
||||||
minPrice: 600.u256,
|
availability = Availability.init(
|
||||||
maxCollateral: 400.u256,
|
totalSize = totalAvailabilitySize,
|
||||||
|
freeSize = totalAvailabilitySize,
|
||||||
|
duration = 60.u256,
|
||||||
|
minPricePerBytePerSecond = minPricePerBytePerSecond,
|
||||||
|
totalCollateral = totalCollateral,
|
||||||
)
|
)
|
||||||
request = StorageRequest(
|
request = StorageRequest(
|
||||||
ask: StorageAsk(
|
ask: StorageAsk(
|
||||||
slots: 4,
|
slots: 4,
|
||||||
slotSize: 100.u256,
|
slotSize: 100.u256,
|
||||||
duration: 60.u256,
|
duration: 60.u256,
|
||||||
reward: 10.u256,
|
pricePerBytePerSecond: minPricePerBytePerSecond,
|
||||||
collateral: 200.u256,
|
collateralPerByte: 1.u256,
|
||||||
),
|
),
|
||||||
content: StorageContent(cid: "some cid"),
|
content: StorageContent(cid: "some cid"),
|
||||||
expiry: (getTime() + initDuration(hours = 1)).toUnix.u256,
|
expiry: (getTime() + initDuration(hours = 1)).toUnix.u256,
|
||||||
@ -210,8 +218,8 @@ asyncchecksuite "Sales":
|
|||||||
|
|
||||||
proc createAvailability() =
|
proc createAvailability() =
|
||||||
let a = waitFor reservations.createAvailability(
|
let a = waitFor reservations.createAvailability(
|
||||||
availability.totalSize, availability.duration, availability.minPrice,
|
availability.totalSize, availability.duration,
|
||||||
availability.maxCollateral,
|
availability.minPricePerBytePerSecond, availability.totalCollateral,
|
||||||
)
|
)
|
||||||
availability = a.get # update id
|
availability = a.get # update id
|
||||||
|
|
||||||
@ -229,7 +237,7 @@ asyncchecksuite "Sales":
|
|||||||
done.complete()
|
done.complete()
|
||||||
|
|
||||||
var request1 = StorageRequest.example
|
var request1 = StorageRequest.example
|
||||||
request1.ask.collateral = request.ask.collateral + 1
|
request1.ask.collateralPerByte = request.ask.collateralPerByte + 1
|
||||||
createAvailability()
|
createAvailability()
|
||||||
# saturate queue
|
# saturate queue
|
||||||
while queue.len < queue.size - 1:
|
while queue.len < queue.size - 1:
|
||||||
@ -383,14 +391,14 @@ asyncchecksuite "Sales":
|
|||||||
check wasIgnored()
|
check wasIgnored()
|
||||||
|
|
||||||
test "ignores request when reward is too low":
|
test "ignores request when reward is too low":
|
||||||
availability.minPrice = request.ask.pricePerSlot + 1
|
availability.minPricePerBytePerSecond = request.ask.pricePerBytePerSecond + 1
|
||||||
createAvailability()
|
createAvailability()
|
||||||
await market.requestStorage(request)
|
await market.requestStorage(request)
|
||||||
check wasIgnored()
|
check wasIgnored()
|
||||||
|
|
||||||
test "ignores request when asked collateral is too high":
|
test "ignores request when asked collateral is too high":
|
||||||
var tooBigCollateral = request
|
var tooBigCollateral = request
|
||||||
tooBigCollateral.ask.collateral = availability.maxCollateral + 1
|
tooBigCollateral.ask.collateralPerByte = requestedCollateralPerByte + 1
|
||||||
createAvailability()
|
createAvailability()
|
||||||
await market.requestStorage(tooBigCollateral)
|
await market.requestStorage(tooBigCollateral)
|
||||||
check wasIgnored()
|
check wasIgnored()
|
||||||
@ -606,7 +614,7 @@ asyncchecksuite "Sales":
|
|||||||
test "deletes inactive reservations on load":
|
test "deletes inactive reservations on load":
|
||||||
createAvailability()
|
createAvailability()
|
||||||
discard await reservations.createReservation(
|
discard await reservations.createReservation(
|
||||||
availability.id, 100.u256, RequestId.example, UInt256.example
|
availability.id, 100.u256, RequestId.example, UInt256.example, UInt256.example
|
||||||
)
|
)
|
||||||
check (await reservations.all(Reservation)).get.len == 1
|
check (await reservations.all(Reservation)).get.len == 1
|
||||||
await sales.load()
|
await sales.load()
|
||||||
|
|||||||
@ -24,12 +24,15 @@ suite "Slot queue start/stop":
|
|||||||
test "starts out not running":
|
test "starts out not running":
|
||||||
check not queue.running
|
check not queue.running
|
||||||
|
|
||||||
|
test "queue starts paused":
|
||||||
|
check queue.paused
|
||||||
|
|
||||||
test "can call start multiple times, and when already running":
|
test "can call start multiple times, and when already running":
|
||||||
queue.start()
|
queue.start()
|
||||||
queue.start()
|
queue.start()
|
||||||
check queue.running
|
check queue.running
|
||||||
|
|
||||||
test "can call stop when alrady stopped":
|
test "can call stop when already stopped":
|
||||||
await queue.stop()
|
await queue.stop()
|
||||||
check not queue.running
|
check not queue.running
|
||||||
|
|
||||||
@ -144,16 +147,16 @@ suite "Slot queue":
|
|||||||
test "correctly compares SlotQueueItems":
|
test "correctly compares SlotQueueItems":
|
||||||
var requestA = StorageRequest.example
|
var requestA = StorageRequest.example
|
||||||
requestA.ask.duration = 1.u256
|
requestA.ask.duration = 1.u256
|
||||||
requestA.ask.reward = 1.u256
|
requestA.ask.pricePerBytePerSecond = 1.u256
|
||||||
check requestA.ask.pricePerSlot == 1.u256
|
check requestA.ask.pricePerSlot == 1.u256 * requestA.ask.slotSize
|
||||||
requestA.ask.collateral = 100000.u256
|
requestA.ask.collateralPerByte = 100000.u256
|
||||||
requestA.expiry = 1001.u256
|
requestA.expiry = 1001.u256
|
||||||
|
|
||||||
var requestB = StorageRequest.example
|
var requestB = StorageRequest.example
|
||||||
requestB.ask.duration = 100.u256
|
requestB.ask.duration = 100.u256
|
||||||
requestB.ask.reward = 1000.u256
|
requestB.ask.pricePerBytePerSecond = 1000.u256
|
||||||
check requestB.ask.pricePerSlot == 100000.u256
|
check requestB.ask.pricePerSlot == 100000.u256 * requestB.ask.slotSize
|
||||||
requestB.ask.collateral = 1.u256
|
requestB.ask.collateralPerByte = 1.u256
|
||||||
requestB.expiry = 1000.u256
|
requestB.expiry = 1000.u256
|
||||||
|
|
||||||
let itemA = SlotQueueItem.init(requestA, 0)
|
let itemA = SlotQueueItem.init(requestA, 0)
|
||||||
@ -168,8 +171,8 @@ suite "Slot queue":
|
|||||||
slotIndex: 0,
|
slotIndex: 0,
|
||||||
slotSize: 1.u256,
|
slotSize: 1.u256,
|
||||||
duration: 1.u256,
|
duration: 1.u256,
|
||||||
reward: 2.u256, # profitability is higher (good)
|
pricePerBytePerSecond: 2.u256, # profitability is higher (good)
|
||||||
collateral: 1.u256,
|
collateralPerByte: 1.u256,
|
||||||
expiry: 1.u256,
|
expiry: 1.u256,
|
||||||
seen: true, # seen (bad), more weight than profitability
|
seen: true, # seen (bad), more weight than profitability
|
||||||
)
|
)
|
||||||
@ -178,8 +181,8 @@ suite "Slot queue":
|
|||||||
slotIndex: 0,
|
slotIndex: 0,
|
||||||
slotSize: 1.u256,
|
slotSize: 1.u256,
|
||||||
duration: 1.u256,
|
duration: 1.u256,
|
||||||
reward: 1.u256, # profitability is lower (bad)
|
pricePerBytePerSecond: 1.u256, # profitability is lower (bad)
|
||||||
collateral: 1.u256,
|
collateralPerByte: 1.u256,
|
||||||
expiry: 1.u256,
|
expiry: 1.u256,
|
||||||
seen: false, # not seen (good)
|
seen: false, # not seen (good)
|
||||||
)
|
)
|
||||||
@ -193,8 +196,8 @@ suite "Slot queue":
|
|||||||
slotIndex: 0,
|
slotIndex: 0,
|
||||||
slotSize: 1.u256,
|
slotSize: 1.u256,
|
||||||
duration: 1.u256,
|
duration: 1.u256,
|
||||||
reward: 1.u256, # reward is lower (bad)
|
pricePerBytePerSecond: 1.u256, # reward is lower (bad)
|
||||||
collateral: 1.u256, # collateral is lower (good)
|
collateralPerByte: 1.u256, # collateral is lower (good)
|
||||||
expiry: 1.u256,
|
expiry: 1.u256,
|
||||||
seen: false,
|
seen: false,
|
||||||
)
|
)
|
||||||
@ -203,8 +206,9 @@ suite "Slot queue":
|
|||||||
slotIndex: 0,
|
slotIndex: 0,
|
||||||
slotSize: 1.u256,
|
slotSize: 1.u256,
|
||||||
duration: 1.u256,
|
duration: 1.u256,
|
||||||
reward: 2.u256, # reward is higher (good), more weight than collateral
|
pricePerBytePerSecond: 2.u256,
|
||||||
collateral: 2.u256, # collateral is higher (bad)
|
# reward is higher (good), more weight than collateral
|
||||||
|
collateralPerByte: 2.u256, # collateral is higher (bad)
|
||||||
expiry: 1.u256,
|
expiry: 1.u256,
|
||||||
seen: false,
|
seen: false,
|
||||||
)
|
)
|
||||||
@ -218,8 +222,8 @@ suite "Slot queue":
|
|||||||
slotIndex: 0,
|
slotIndex: 0,
|
||||||
slotSize: 1.u256,
|
slotSize: 1.u256,
|
||||||
duration: 1.u256,
|
duration: 1.u256,
|
||||||
reward: 1.u256,
|
pricePerBytePerSecond: 1.u256,
|
||||||
collateral: 2.u256, # collateral is higher (bad)
|
collateralPerByte: 2.u256, # collateral is higher (bad)
|
||||||
expiry: 2.u256, # expiry is longer (good)
|
expiry: 2.u256, # expiry is longer (good)
|
||||||
seen: false,
|
seen: false,
|
||||||
)
|
)
|
||||||
@ -228,8 +232,8 @@ suite "Slot queue":
|
|||||||
slotIndex: 0,
|
slotIndex: 0,
|
||||||
slotSize: 1.u256,
|
slotSize: 1.u256,
|
||||||
duration: 1.u256,
|
duration: 1.u256,
|
||||||
reward: 1.u256,
|
pricePerBytePerSecond: 1.u256,
|
||||||
collateral: 1.u256, # collateral is lower (good), more weight than expiry
|
collateralPerByte: 1.u256, # collateral is lower (good), more weight than expiry
|
||||||
expiry: 1.u256, # expiry is shorter (bad)
|
expiry: 1.u256, # expiry is shorter (bad)
|
||||||
seen: false,
|
seen: false,
|
||||||
)
|
)
|
||||||
@ -243,8 +247,8 @@ suite "Slot queue":
|
|||||||
slotIndex: 0,
|
slotIndex: 0,
|
||||||
slotSize: 1.u256, # slotSize is smaller (good)
|
slotSize: 1.u256, # slotSize is smaller (good)
|
||||||
duration: 1.u256,
|
duration: 1.u256,
|
||||||
reward: 1.u256,
|
pricePerBytePerSecond: 1.u256,
|
||||||
collateral: 1.u256,
|
collateralPerByte: 1.u256,
|
||||||
expiry: 1.u256, # expiry is shorter (bad)
|
expiry: 1.u256, # expiry is shorter (bad)
|
||||||
seen: false,
|
seen: false,
|
||||||
)
|
)
|
||||||
@ -253,8 +257,8 @@ suite "Slot queue":
|
|||||||
slotIndex: 0,
|
slotIndex: 0,
|
||||||
slotSize: 2.u256, # slotSize is larger (bad)
|
slotSize: 2.u256, # slotSize is larger (bad)
|
||||||
duration: 1.u256,
|
duration: 1.u256,
|
||||||
reward: 1.u256,
|
pricePerBytePerSecond: 1.u256,
|
||||||
collateral: 1.u256,
|
collateralPerByte: 1.u256,
|
||||||
expiry: 2.u256, # expiry is longer (good), more weight than slotSize
|
expiry: 2.u256, # expiry is longer (good), more weight than slotSize
|
||||||
seen: false,
|
seen: false,
|
||||||
)
|
)
|
||||||
@ -268,8 +272,8 @@ suite "Slot queue":
|
|||||||
slotIndex: 0,
|
slotIndex: 0,
|
||||||
slotSize: 2.u256, # slotSize is larger (bad)
|
slotSize: 2.u256, # slotSize is larger (bad)
|
||||||
duration: 1.u256,
|
duration: 1.u256,
|
||||||
reward: 1.u256,
|
pricePerBytePerSecond: 1.u256,
|
||||||
collateral: 1.u256,
|
collateralPerByte: 1.u256,
|
||||||
expiry: 1.u256, # expiry is shorter (bad)
|
expiry: 1.u256, # expiry is shorter (bad)
|
||||||
seen: false,
|
seen: false,
|
||||||
)
|
)
|
||||||
@ -278,13 +282,13 @@ suite "Slot queue":
|
|||||||
slotIndex: 0,
|
slotIndex: 0,
|
||||||
slotSize: 1.u256, # slotSize is smaller (good)
|
slotSize: 1.u256, # slotSize is smaller (good)
|
||||||
duration: 1.u256,
|
duration: 1.u256,
|
||||||
reward: 1.u256,
|
pricePerBytePerSecond: 1.u256,
|
||||||
collateral: 1.u256,
|
collateralPerByte: 1.u256,
|
||||||
expiry: 1.u256,
|
expiry: 1.u256,
|
||||||
seen: false,
|
seen: false,
|
||||||
)
|
)
|
||||||
|
|
||||||
check itemB.toSlotQueueItem < itemA.toSlotQueueItem # < indicates higher priority
|
check itemA.toSlotQueueItem < itemB.toSlotQueueItem # < indicates higher priority
|
||||||
|
|
||||||
test "expands available all possible slot indices on init":
|
test "expands available all possible slot indices on init":
|
||||||
let request = StorageRequest.example
|
let request = StorageRequest.example
|
||||||
@ -322,7 +326,7 @@ suite "Slot queue":
|
|||||||
newSlotQueue(maxSize = 8, maxWorkers = 1, processSlotDelay = 10.millis)
|
newSlotQueue(maxSize = 8, maxWorkers = 1, processSlotDelay = 10.millis)
|
||||||
let request0 = StorageRequest.example
|
let request0 = StorageRequest.example
|
||||||
var request1 = StorageRequest.example
|
var request1 = StorageRequest.example
|
||||||
request1.ask.collateral += 1.u256
|
request1.ask.collateralPerByte += 1.u256
|
||||||
let items0 = SlotQueueItem.init(request0)
|
let items0 = SlotQueueItem.init(request0)
|
||||||
let items1 = SlotQueueItem.init(request1)
|
let items1 = SlotQueueItem.init(request1)
|
||||||
check queue.push(items0).isOk
|
check queue.push(items0).isOk
|
||||||
@ -332,8 +336,8 @@ suite "Slot queue":
|
|||||||
check populated.slotIndex == 12'u16
|
check populated.slotIndex == 12'u16
|
||||||
check populated.slotSize == request1.ask.slotSize
|
check populated.slotSize == request1.ask.slotSize
|
||||||
check populated.duration == request1.ask.duration
|
check populated.duration == request1.ask.duration
|
||||||
check populated.reward == request1.ask.reward
|
check populated.pricePerBytePerSecond == request1.ask.pricePerBytePerSecond
|
||||||
check populated.collateral == request1.ask.collateral
|
check populated.collateralPerByte == request1.ask.collateralPerByte
|
||||||
|
|
||||||
test "does not find exisiting request metadata":
|
test "does not find exisiting request metadata":
|
||||||
newSlotQueue(maxSize = 2, maxWorkers = 2)
|
newSlotQueue(maxSize = 2, maxWorkers = 2)
|
||||||
@ -394,7 +398,7 @@ suite "Slot queue":
|
|||||||
newSlotQueue(maxSize = 8, maxWorkers = 1, processSlotDelay = 10.millis)
|
newSlotQueue(maxSize = 8, maxWorkers = 1, processSlotDelay = 10.millis)
|
||||||
let request0 = StorageRequest.example
|
let request0 = StorageRequest.example
|
||||||
var request1 = StorageRequest.example
|
var request1 = StorageRequest.example
|
||||||
request1.ask.collateral += 1.u256
|
request1.ask.collateralPerByte += 1.u256
|
||||||
let items0 = SlotQueueItem.init(request0)
|
let items0 = SlotQueueItem.init(request0)
|
||||||
let items1 = SlotQueueItem.init(request1)
|
let items1 = SlotQueueItem.init(request1)
|
||||||
check queue.push(items0).isOk
|
check queue.push(items0).isOk
|
||||||
@ -408,7 +412,7 @@ suite "Slot queue":
|
|||||||
newSlotQueue(maxSize = 8, maxWorkers = 1, processSlotDelay = 10.millis)
|
newSlotQueue(maxSize = 8, maxWorkers = 1, processSlotDelay = 10.millis)
|
||||||
let request0 = StorageRequest.example
|
let request0 = StorageRequest.example
|
||||||
var request1 = StorageRequest.example
|
var request1 = StorageRequest.example
|
||||||
request1.ask.collateral += 1.u256
|
request1.ask.collateralPerByte += 1.u256
|
||||||
let items0 = SlotQueueItem.init(request0)
|
let items0 = SlotQueueItem.init(request0)
|
||||||
let items1 = SlotQueueItem.init(request1)
|
let items1 = SlotQueueItem.init(request1)
|
||||||
check queue.push(items0).isOk
|
check queue.push(items0).isOk
|
||||||
@ -424,11 +428,11 @@ suite "Slot queue":
|
|||||||
var request3 = StorageRequest.example
|
var request3 = StorageRequest.example
|
||||||
var request4 = StorageRequest.example
|
var request4 = StorageRequest.example
|
||||||
var request5 = StorageRequest.example
|
var request5 = StorageRequest.example
|
||||||
request1.ask.collateral = request0.ask.collateral + 1
|
request1.ask.collateralPerByte = request0.ask.collateralPerByte + 1
|
||||||
request2.ask.collateral = request1.ask.collateral + 1
|
request2.ask.collateralPerByte = request1.ask.collateralPerByte + 1
|
||||||
request3.ask.collateral = request2.ask.collateral + 1
|
request3.ask.collateralPerByte = request2.ask.collateralPerByte + 1
|
||||||
request4.ask.collateral = request3.ask.collateral + 1
|
request4.ask.collateralPerByte = request3.ask.collateralPerByte + 1
|
||||||
request5.ask.collateral = request4.ask.collateral + 1
|
request5.ask.collateralPerByte = request4.ask.collateralPerByte + 1
|
||||||
let item0 = SlotQueueItem.init(request0, 0)
|
let item0 = SlotQueueItem.init(request0, 0)
|
||||||
let item1 = SlotQueueItem.init(request1, 0)
|
let item1 = SlotQueueItem.init(request1, 0)
|
||||||
let item2 = SlotQueueItem.init(request2, 0)
|
let item2 = SlotQueueItem.init(request2, 0)
|
||||||
@ -439,19 +443,19 @@ suite "Slot queue":
|
|||||||
check queue.push(@[item0, item1, item2, item3, item4, item5]).isOk
|
check queue.push(@[item0, item1, item2, item3, item4, item5]).isOk
|
||||||
check queue.contains(item5)
|
check queue.contains(item5)
|
||||||
|
|
||||||
test "sorts items by profitability ascending (higher pricePerSlot = higher priority)":
|
test "sorts items by profitability descending (higher pricePerBytePerSecond == higher priority == goes first in the list)":
|
||||||
var request = StorageRequest.example
|
var request = StorageRequest.example
|
||||||
let item0 = SlotQueueItem.init(request, 0)
|
let item0 = SlotQueueItem.init(request, 0)
|
||||||
request.ask.reward += 1.u256
|
request.ask.pricePerBytePerSecond += 1.u256
|
||||||
let item1 = SlotQueueItem.init(request, 1)
|
let item1 = SlotQueueItem.init(request, 1)
|
||||||
check item1 < item0
|
check item1 < item0
|
||||||
|
|
||||||
test "sorts items by collateral ascending (less required collateral = higher priority)":
|
test "sorts items by collateral ascending (higher required collateralPerByte = lower priority == comes later in the list)":
|
||||||
var request = StorageRequest.example
|
var request = StorageRequest.example
|
||||||
let item0 = SlotQueueItem.init(request, 0)
|
let item0 = SlotQueueItem.init(request, 0)
|
||||||
request.ask.collateral -= 1.u256
|
request.ask.collateralPerByte += 1.u256
|
||||||
let item1 = SlotQueueItem.init(request, 1)
|
let item1 = SlotQueueItem.init(request, 1)
|
||||||
check item1 < item0
|
check item1 > item0
|
||||||
|
|
||||||
test "sorts items by expiry descending (longer expiry = higher priority)":
|
test "sorts items by expiry descending (longer expiry = higher priority)":
|
||||||
var request = StorageRequest.example
|
var request = StorageRequest.example
|
||||||
@ -460,10 +464,10 @@ suite "Slot queue":
|
|||||||
let item1 = SlotQueueItem.init(request, 1)
|
let item1 = SlotQueueItem.init(request, 1)
|
||||||
check item1 < item0
|
check item1 < item0
|
||||||
|
|
||||||
test "sorts items by slot size ascending (smaller dataset = higher priority)":
|
test "sorts items by slot size descending (bigger dataset = higher profitability = higher priority)":
|
||||||
var request = StorageRequest.example
|
var request = StorageRequest.example
|
||||||
let item0 = SlotQueueItem.init(request, 0)
|
let item0 = SlotQueueItem.init(request, 0)
|
||||||
request.ask.slotSize -= 1.u256
|
request.ask.slotSize += 1.u256
|
||||||
let item1 = SlotQueueItem.init(request, 1)
|
let item1 = SlotQueueItem.init(request, 1)
|
||||||
check item1 < item0
|
check item1 < item0
|
||||||
|
|
||||||
@ -480,17 +484,17 @@ suite "Slot queue":
|
|||||||
check queue.push(item).isOk
|
check queue.push(item).isOk
|
||||||
check eventually onProcessSlotCalledWith == @[(item.requestId, item.slotIndex)]
|
check eventually onProcessSlotCalledWith == @[(item.requestId, item.slotIndex)]
|
||||||
|
|
||||||
test "should process items in correct order":
|
test "processes items in order of addition when only one item is added at a time":
|
||||||
newSlotQueue(maxSize = 2, maxWorkers = 2)
|
newSlotQueue(maxSize = 2, maxWorkers = 2)
|
||||||
# sleeping after push allows the slotqueue loop to iterate,
|
# sleeping after push allows the slotqueue loop to iterate,
|
||||||
# calling the callback for each pushed/updated item
|
# calling the callback for each pushed/updated item
|
||||||
var request = StorageRequest.example
|
var request = StorageRequest.example
|
||||||
let item0 = SlotQueueItem.init(request, 0)
|
let item0 = SlotQueueItem.init(request, 0)
|
||||||
request.ask.reward += 1.u256
|
request.ask.pricePerBytePerSecond += 1.u256
|
||||||
let item1 = SlotQueueItem.init(request, 1)
|
let item1 = SlotQueueItem.init(request, 1)
|
||||||
request.ask.reward += 1.u256
|
request.ask.pricePerBytePerSecond += 1.u256
|
||||||
let item2 = SlotQueueItem.init(request, 2)
|
let item2 = SlotQueueItem.init(request, 2)
|
||||||
request.ask.reward += 1.u256
|
request.ask.pricePerBytePerSecond += 1.u256
|
||||||
let item3 = SlotQueueItem.init(request, 3)
|
let item3 = SlotQueueItem.init(request, 3)
|
||||||
|
|
||||||
check queue.push(item0).isOk
|
check queue.push(item0).isOk
|
||||||
@ -511,9 +515,35 @@ suite "Slot queue":
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
test "queue starts paused":
|
test "should process items in correct order according to the queue invariant when more than one item is added at a time":
|
||||||
newSlotQueue(maxSize = 4, maxWorkers = 4)
|
newSlotQueue(maxSize = 4, maxWorkers = 2)
|
||||||
check queue.paused
|
# sleeping after push allows the slotqueue loop to iterate,
|
||||||
|
# calling the callback for each pushed/updated item
|
||||||
|
var request = StorageRequest.example
|
||||||
|
let item0 = SlotQueueItem.init(request, 0)
|
||||||
|
request.ask.pricePerBytePerSecond += 1.u256
|
||||||
|
let item1 = SlotQueueItem.init(request, 1)
|
||||||
|
request.ask.pricePerBytePerSecond += 1.u256
|
||||||
|
let item2 = SlotQueueItem.init(request, 2)
|
||||||
|
request.ask.pricePerBytePerSecond += 1.u256
|
||||||
|
let item3 = SlotQueueItem.init(request, 3)
|
||||||
|
|
||||||
|
check queue.push(item0).isOk
|
||||||
|
check queue.push(item1).isOk
|
||||||
|
check queue.push(item2).isOk
|
||||||
|
check queue.push(item3).isOk
|
||||||
|
|
||||||
|
await sleepAsync(1.millis)
|
||||||
|
|
||||||
|
check eventually (
|
||||||
|
onProcessSlotCalledWith ==
|
||||||
|
@[
|
||||||
|
(item3.requestId, item3.slotIndex),
|
||||||
|
(item2.requestId, item2.slotIndex),
|
||||||
|
(item1.requestId, item1.slotIndex),
|
||||||
|
(item0.requestId, item0.slotIndex),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
test "pushing items to queue unpauses queue":
|
test "pushing items to queue unpauses queue":
|
||||||
newSlotQueue(maxSize = 4, maxWorkers = 4)
|
newSlotQueue(maxSize = 4, maxWorkers = 4)
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import ./states/testunknown
|
import ./states/testunknown
|
||||||
import ./states/testdownloading
|
import ./states/testdownloading
|
||||||
import ./states/testfilling
|
import ./states/testfilling
|
||||||
|
import ./states/testpayout
|
||||||
import ./states/testfinished
|
import ./states/testfinished
|
||||||
import ./states/testinitialproving
|
import ./states/testinitialproving
|
||||||
import ./states/testfilled
|
import ./states/testfilled
|
||||||
|
|||||||
@ -30,7 +30,7 @@ asyncchecksuite "Purchasing":
|
|||||||
slots: uint8.example.uint64,
|
slots: uint8.example.uint64,
|
||||||
slotSize: uint32.example.u256,
|
slotSize: uint32.example.u256,
|
||||||
duration: uint16.example.u256,
|
duration: uint16.example.u256,
|
||||||
reward: uint8.example.u256,
|
pricePerBytePerSecond: uint8.example.u256,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -46,7 +46,8 @@ asyncchecksuite "Purchasing":
|
|||||||
check market.requested[0].ask.slots == request.ask.slots
|
check market.requested[0].ask.slots == request.ask.slots
|
||||||
check market.requested[0].ask.slotSize == request.ask.slotSize
|
check market.requested[0].ask.slotSize == request.ask.slotSize
|
||||||
check market.requested[0].ask.duration == request.ask.duration
|
check market.requested[0].ask.duration == request.ask.duration
|
||||||
check market.requested[0].ask.reward == request.ask.reward
|
check market.requested[0].ask.pricePerBytePerSecond ==
|
||||||
|
request.ask.pricePerBytePerSecond
|
||||||
|
|
||||||
test "remembers purchases":
|
test "remembers purchases":
|
||||||
let purchase1 = await purchasing.purchase(request)
|
let purchase1 = await purchasing.purchase(request)
|
||||||
@ -131,7 +132,7 @@ checksuite "Purchasing state machine":
|
|||||||
slots: uint8.example.uint64,
|
slots: uint8.example.uint64,
|
||||||
slotSize: uint32.example.u256,
|
slotSize: uint32.example.u256,
|
||||||
duration: uint16.example.u256,
|
duration: uint16.example.u256,
|
||||||
reward: uint8.example.u256,
|
pricePerBytePerSecond: uint8.example.u256,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -22,7 +22,7 @@ asyncchecksuite "validation":
|
|||||||
let validationGroups = ValidationGroups(8).some
|
let validationGroups = ValidationGroups(8).some
|
||||||
let slot = Slot.example
|
let slot = Slot.example
|
||||||
let proof = Groth16Proof.example
|
let proof = Groth16Proof.example
|
||||||
let collateral = slot.request.ask.collateral
|
let collateral = slot.request.ask.collateralPerSlot
|
||||||
|
|
||||||
var market: MockMarket
|
var market: MockMarket
|
||||||
var clock: MockClock
|
var clock: MockClock
|
||||||
|
|||||||
@ -19,7 +19,7 @@ ethersuite "Marketplace contracts":
|
|||||||
var filledAt: UInt256
|
var filledAt: UInt256
|
||||||
|
|
||||||
proc expectedPayout(endTimestamp: UInt256): UInt256 =
|
proc expectedPayout(endTimestamp: UInt256): UInt256 =
|
||||||
return (endTimestamp - filledAt) * request.ask.reward
|
return (endTimestamp - filledAt) * request.ask.pricePerSlotPerSecond()
|
||||||
|
|
||||||
proc switchAccount(account: Signer) =
|
proc switchAccount(account: Signer) =
|
||||||
marketplace = marketplace.connect(account)
|
marketplace = marketplace.connect(account)
|
||||||
@ -44,10 +44,11 @@ ethersuite "Marketplace contracts":
|
|||||||
request.client = await client.getAddress()
|
request.client = await client.getAddress()
|
||||||
|
|
||||||
switchAccount(client)
|
switchAccount(client)
|
||||||
discard await token.approve(marketplace.address, request.price).confirm(1)
|
discard await token.approve(marketplace.address, request.totalPrice).confirm(1)
|
||||||
discard await marketplace.requestStorage(request).confirm(1)
|
discard await marketplace.requestStorage(request).confirm(1)
|
||||||
switchAccount(host)
|
switchAccount(host)
|
||||||
discard await token.approve(marketplace.address, request.ask.collateral).confirm(1)
|
discard
|
||||||
|
await token.approve(marketplace.address, request.ask.collateralPerSlot).confirm(1)
|
||||||
discard await marketplace.reserveSlot(request.id, 0.u256).confirm(1)
|
discard await marketplace.reserveSlot(request.id, 0.u256).confirm(1)
|
||||||
let receipt = await marketplace.fillSlot(request.id, 0.u256, proof).confirm(1)
|
let receipt = await marketplace.fillSlot(request.id, 0.u256, proof).confirm(1)
|
||||||
filledAt = await ethProvider.blockTime(BlockTag.init(!receipt.blockNumber))
|
filledAt = await ethProvider.blockTime(BlockTag.init(!receipt.blockNumber))
|
||||||
@ -65,8 +66,9 @@ ethersuite "Marketplace contracts":
|
|||||||
|
|
||||||
proc startContract() {.async.} =
|
proc startContract() {.async.} =
|
||||||
for slotIndex in 1 ..< request.ask.slots:
|
for slotIndex in 1 ..< request.ask.slots:
|
||||||
discard
|
discard await token
|
||||||
await token.approve(marketplace.address, request.ask.collateral).confirm(1)
|
.approve(marketplace.address, request.ask.collateralPerSlot)
|
||||||
|
.confirm(1)
|
||||||
discard await marketplace.reserveSlot(request.id, slotIndex.u256).confirm(1)
|
discard await marketplace.reserveSlot(request.id, slotIndex.u256).confirm(1)
|
||||||
discard await marketplace.fillSlot(request.id, slotIndex.u256, proof).confirm(1)
|
discard await marketplace.fillSlot(request.id, slotIndex.u256, proof).confirm(1)
|
||||||
|
|
||||||
@ -94,7 +96,7 @@ ethersuite "Marketplace contracts":
|
|||||||
discard await marketplace.freeSlot(slotId).confirm(1)
|
discard await marketplace.freeSlot(slotId).confirm(1)
|
||||||
let endBalance = await token.balanceOf(address)
|
let endBalance = await token.balanceOf(address)
|
||||||
check endBalance ==
|
check endBalance ==
|
||||||
(startBalance + expectedPayout(requestEnd.u256) + request.ask.collateral)
|
(startBalance + expectedPayout(requestEnd.u256) + request.ask.collateralPerSlot)
|
||||||
|
|
||||||
test "can be paid out at the end, specifying reward and collateral recipient":
|
test "can be paid out at the end, specifying reward and collateral recipient":
|
||||||
switchAccount(host)
|
switchAccount(host)
|
||||||
@ -114,7 +116,8 @@ ethersuite "Marketplace contracts":
|
|||||||
|
|
||||||
check endBalanceHost == startBalanceHost
|
check endBalanceHost == startBalanceHost
|
||||||
check endBalanceReward == (startBalanceReward + expectedPayout(requestEnd.u256))
|
check endBalanceReward == (startBalanceReward + expectedPayout(requestEnd.u256))
|
||||||
check endBalanceCollateral == (startBalanceCollateral + request.ask.collateral)
|
check endBalanceCollateral ==
|
||||||
|
(startBalanceCollateral + request.ask.collateralPerSlot)
|
||||||
|
|
||||||
test "cannot mark proofs missing for cancelled request":
|
test "cannot mark proofs missing for cancelled request":
|
||||||
let expiry = await marketplace.requestExpiry(request.id)
|
let expiry = await marketplace.requestExpiry(request.id)
|
||||||
|
|||||||
@ -32,7 +32,7 @@ ethersuite "On-Chain Market":
|
|||||||
proc expectedPayout(
|
proc expectedPayout(
|
||||||
r: StorageRequest, startTimestamp: UInt256, endTimestamp: UInt256
|
r: StorageRequest, startTimestamp: UInt256, endTimestamp: UInt256
|
||||||
): UInt256 =
|
): UInt256 =
|
||||||
return (endTimestamp - startTimestamp) * r.ask.reward
|
return (endTimestamp - startTimestamp) * r.ask.pricePerSlotPerSecond
|
||||||
|
|
||||||
proc switchAccount(account: Signer) =
|
proc switchAccount(account: Signer) =
|
||||||
marketplace = marketplace.connect(account)
|
marketplace = marketplace.connect(account)
|
||||||
@ -118,7 +118,7 @@ ethersuite "On-Chain Market":
|
|||||||
|
|
||||||
let endBalanceClient = await token.balanceOf(clientAddress)
|
let endBalanceClient = await token.balanceOf(clientAddress)
|
||||||
|
|
||||||
check endBalanceClient == (startBalanceClient + request.price)
|
check endBalanceClient == (startBalanceClient + request.totalPrice)
|
||||||
|
|
||||||
test "supports request subscriptions":
|
test "supports request subscriptions":
|
||||||
var receivedIds: seq[RequestId]
|
var receivedIds: seq[RequestId]
|
||||||
@ -135,19 +135,19 @@ ethersuite "On-Chain Market":
|
|||||||
test "supports filling of slots":
|
test "supports filling of slots":
|
||||||
await market.requestStorage(request)
|
await market.requestStorage(request)
|
||||||
await market.reserveSlot(request.id, slotIndex)
|
await market.reserveSlot(request.id, slotIndex)
|
||||||
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateral)
|
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateralPerSlot)
|
||||||
|
|
||||||
test "can retrieve host that filled slot":
|
test "can retrieve host that filled slot":
|
||||||
await market.requestStorage(request)
|
await market.requestStorage(request)
|
||||||
check (await market.getHost(request.id, slotIndex)) == none Address
|
check (await market.getHost(request.id, slotIndex)) == none Address
|
||||||
await market.reserveSlot(request.id, slotIndex)
|
await market.reserveSlot(request.id, slotIndex)
|
||||||
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateral)
|
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateralPerSlot)
|
||||||
check (await market.getHost(request.id, slotIndex)) == some accounts[0]
|
check (await market.getHost(request.id, slotIndex)) == some accounts[0]
|
||||||
|
|
||||||
test "supports freeing a slot":
|
test "supports freeing a slot":
|
||||||
await market.requestStorage(request)
|
await market.requestStorage(request)
|
||||||
await market.reserveSlot(request.id, slotIndex)
|
await market.reserveSlot(request.id, slotIndex)
|
||||||
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateral)
|
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateralPerSlot)
|
||||||
await market.freeSlot(slotId(request.id, slotIndex))
|
await market.freeSlot(slotId(request.id, slotIndex))
|
||||||
check (await market.getHost(request.id, slotIndex)) == none Address
|
check (await market.getHost(request.id, slotIndex)) == none Address
|
||||||
|
|
||||||
@ -160,7 +160,7 @@ ethersuite "On-Chain Market":
|
|||||||
test "submits proofs":
|
test "submits proofs":
|
||||||
await market.requestStorage(request)
|
await market.requestStorage(request)
|
||||||
await market.reserveSlot(request.id, slotIndex)
|
await market.reserveSlot(request.id, slotIndex)
|
||||||
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateral)
|
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateralPerSlot)
|
||||||
await advanceToNextPeriod()
|
await advanceToNextPeriod()
|
||||||
await market.submitProof(slotId(request.id, slotIndex), proof)
|
await market.submitProof(slotId(request.id, slotIndex), proof)
|
||||||
|
|
||||||
@ -168,7 +168,7 @@ ethersuite "On-Chain Market":
|
|||||||
let slotId = slotId(request, slotIndex)
|
let slotId = slotId(request, slotIndex)
|
||||||
await market.requestStorage(request)
|
await market.requestStorage(request)
|
||||||
await market.reserveSlot(request.id, slotIndex)
|
await market.reserveSlot(request.id, slotIndex)
|
||||||
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateral)
|
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateralPerSlot)
|
||||||
await waitUntilProofRequired(slotId)
|
await waitUntilProofRequired(slotId)
|
||||||
let missingPeriod = periodicity.periodOf(await ethProvider.currentTime())
|
let missingPeriod = periodicity.periodOf(await ethProvider.currentTime())
|
||||||
await advanceToNextPeriod()
|
await advanceToNextPeriod()
|
||||||
@ -179,7 +179,7 @@ ethersuite "On-Chain Market":
|
|||||||
let slotId = slotId(request, slotIndex)
|
let slotId = slotId(request, slotIndex)
|
||||||
await market.requestStorage(request)
|
await market.requestStorage(request)
|
||||||
await market.reserveSlot(request.id, slotIndex)
|
await market.reserveSlot(request.id, slotIndex)
|
||||||
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateral)
|
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateralPerSlot)
|
||||||
await waitUntilProofRequired(slotId)
|
await waitUntilProofRequired(slotId)
|
||||||
let missingPeriod = periodicity.periodOf(await ethProvider.currentTime())
|
let missingPeriod = periodicity.periodOf(await ethProvider.currentTime())
|
||||||
await advanceToNextPeriod()
|
await advanceToNextPeriod()
|
||||||
@ -195,7 +195,7 @@ ethersuite "On-Chain Market":
|
|||||||
|
|
||||||
let subscription = await market.subscribeSlotFilled(onSlotFilled)
|
let subscription = await market.subscribeSlotFilled(onSlotFilled)
|
||||||
await market.reserveSlot(request.id, slotIndex)
|
await market.reserveSlot(request.id, slotIndex)
|
||||||
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateral)
|
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateralPerSlot)
|
||||||
check eventually receivedIds == @[request.id] and receivedSlotIndices == @[
|
check eventually receivedIds == @[request.id] and receivedSlotIndices == @[
|
||||||
slotIndex
|
slotIndex
|
||||||
]
|
]
|
||||||
@ -211,16 +211,16 @@ ethersuite "On-Chain Market":
|
|||||||
let subscription =
|
let subscription =
|
||||||
await market.subscribeSlotFilled(request.id, slotIndex, onSlotFilled)
|
await market.subscribeSlotFilled(request.id, slotIndex, onSlotFilled)
|
||||||
await market.reserveSlot(request.id, otherSlot)
|
await market.reserveSlot(request.id, otherSlot)
|
||||||
await market.fillSlot(request.id, otherSlot, proof, request.ask.collateral)
|
await market.fillSlot(request.id, otherSlot, proof, request.ask.collateralPerSlot)
|
||||||
await market.reserveSlot(request.id, slotIndex)
|
await market.reserveSlot(request.id, slotIndex)
|
||||||
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateral)
|
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateralPerSlot)
|
||||||
check eventually receivedSlotIndices == @[slotIndex]
|
check eventually receivedSlotIndices == @[slotIndex]
|
||||||
await subscription.unsubscribe()
|
await subscription.unsubscribe()
|
||||||
|
|
||||||
test "supports slot freed subscriptions":
|
test "supports slot freed subscriptions":
|
||||||
await market.requestStorage(request)
|
await market.requestStorage(request)
|
||||||
await market.reserveSlot(request.id, slotIndex)
|
await market.reserveSlot(request.id, slotIndex)
|
||||||
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateral)
|
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateralPerSlot)
|
||||||
var receivedRequestIds: seq[RequestId] = @[]
|
var receivedRequestIds: seq[RequestId] = @[]
|
||||||
var receivedIdxs: seq[UInt256] = @[]
|
var receivedIdxs: seq[UInt256] = @[]
|
||||||
proc onSlotFreed(requestId: RequestId, idx: UInt256) =
|
proc onSlotFreed(requestId: RequestId, idx: UInt256) =
|
||||||
@ -269,7 +269,9 @@ ethersuite "On-Chain Market":
|
|||||||
let subscription = await market.subscribeFulfillment(request.id, onFulfillment)
|
let subscription = await market.subscribeFulfillment(request.id, onFulfillment)
|
||||||
for slotIndex in 0 ..< request.ask.slots:
|
for slotIndex in 0 ..< request.ask.slots:
|
||||||
await market.reserveSlot(request.id, slotIndex.u256)
|
await market.reserveSlot(request.id, slotIndex.u256)
|
||||||
await market.fillSlot(request.id, slotIndex.u256, proof, request.ask.collateral)
|
await market.fillSlot(
|
||||||
|
request.id, slotIndex.u256, proof, request.ask.collateralPerSlot
|
||||||
|
)
|
||||||
check eventually receivedIds == @[request.id]
|
check eventually receivedIds == @[request.id]
|
||||||
await subscription.unsubscribe()
|
await subscription.unsubscribe()
|
||||||
|
|
||||||
@ -288,11 +290,13 @@ ethersuite "On-Chain Market":
|
|||||||
|
|
||||||
for slotIndex in 0 ..< request.ask.slots:
|
for slotIndex in 0 ..< request.ask.slots:
|
||||||
await market.reserveSlot(request.id, slotIndex.u256)
|
await market.reserveSlot(request.id, slotIndex.u256)
|
||||||
await market.fillSlot(request.id, slotIndex.u256, proof, request.ask.collateral)
|
await market.fillSlot(
|
||||||
|
request.id, slotIndex.u256, proof, request.ask.collateralPerSlot
|
||||||
|
)
|
||||||
for slotIndex in 0 ..< otherRequest.ask.slots:
|
for slotIndex in 0 ..< otherRequest.ask.slots:
|
||||||
await market.reserveSlot(otherRequest.id, slotIndex.u256)
|
await market.reserveSlot(otherRequest.id, slotIndex.u256)
|
||||||
await market.fillSlot(
|
await market.fillSlot(
|
||||||
otherRequest.id, slotIndex.u256, proof, otherRequest.ask.collateral
|
otherRequest.id, slotIndex.u256, proof, otherRequest.ask.collateralPerSlot
|
||||||
)
|
)
|
||||||
|
|
||||||
check eventually receivedIds == @[request.id]
|
check eventually receivedIds == @[request.id]
|
||||||
@ -325,7 +329,9 @@ ethersuite "On-Chain Market":
|
|||||||
|
|
||||||
for slotIndex in 0 ..< request.ask.slots:
|
for slotIndex in 0 ..< request.ask.slots:
|
||||||
await market.reserveSlot(request.id, slotIndex.u256)
|
await market.reserveSlot(request.id, slotIndex.u256)
|
||||||
await market.fillSlot(request.id, slotIndex.u256, proof, request.ask.collateral)
|
await market.fillSlot(
|
||||||
|
request.id, slotIndex.u256, proof, request.ask.collateralPerSlot
|
||||||
|
)
|
||||||
for slotIndex in 0 .. request.ask.maxSlotLoss:
|
for slotIndex in 0 .. request.ask.maxSlotLoss:
|
||||||
let slotId = request.slotId(slotIndex.u256)
|
let slotId = request.slotId(slotIndex.u256)
|
||||||
while true:
|
while true:
|
||||||
@ -360,7 +366,7 @@ ethersuite "On-Chain Market":
|
|||||||
test "supports proof submission subscriptions":
|
test "supports proof submission subscriptions":
|
||||||
await market.requestStorage(request)
|
await market.requestStorage(request)
|
||||||
await market.reserveSlot(request.id, slotIndex)
|
await market.reserveSlot(request.id, slotIndex)
|
||||||
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateral)
|
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateralPerSlot)
|
||||||
await advanceToNextPeriod()
|
await advanceToNextPeriod()
|
||||||
var receivedIds: seq[SlotId]
|
var receivedIds: seq[SlotId]
|
||||||
proc onProofSubmission(id: SlotId) =
|
proc onProofSubmission(id: SlotId) =
|
||||||
@ -388,15 +394,19 @@ ethersuite "On-Chain Market":
|
|||||||
await market.requestStorage(request)
|
await market.requestStorage(request)
|
||||||
for slotIndex in 0 ..< request.ask.slots:
|
for slotIndex in 0 ..< request.ask.slots:
|
||||||
await market.reserveSlot(request.id, slotIndex.u256)
|
await market.reserveSlot(request.id, slotIndex.u256)
|
||||||
await market.fillSlot(request.id, slotIndex.u256, proof, request.ask.collateral)
|
await market.fillSlot(
|
||||||
|
request.id, slotIndex.u256, proof, request.ask.collateralPerSlot
|
||||||
|
)
|
||||||
check (await market.requestState(request.id)) == some RequestState.Started
|
check (await market.requestState(request.id)) == some RequestState.Started
|
||||||
|
|
||||||
test "can retrieve active slots":
|
test "can retrieve active slots":
|
||||||
await market.requestStorage(request)
|
await market.requestStorage(request)
|
||||||
await market.reserveSlot(request.id, slotIndex - 1)
|
await market.reserveSlot(request.id, slotIndex - 1)
|
||||||
await market.fillSlot(request.id, slotIndex - 1, proof, request.ask.collateral)
|
await market.fillSlot(
|
||||||
|
request.id, slotIndex - 1, proof, request.ask.collateralPerSlot
|
||||||
|
)
|
||||||
await market.reserveSlot(request.id, slotIndex)
|
await market.reserveSlot(request.id, slotIndex)
|
||||||
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateral)
|
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateralPerSlot)
|
||||||
let slotId1 = request.slotId(slotIndex - 1)
|
let slotId1 = request.slotId(slotIndex - 1)
|
||||||
let slotId2 = request.slotId(slotIndex)
|
let slotId2 = request.slotId(slotIndex)
|
||||||
check (await market.mySlots()) == @[slotId1, slotId2]
|
check (await market.mySlots()) == @[slotId1, slotId2]
|
||||||
@ -409,7 +419,7 @@ ethersuite "On-Chain Market":
|
|||||||
test "can retrieve request details from slot id":
|
test "can retrieve request details from slot id":
|
||||||
await market.requestStorage(request)
|
await market.requestStorage(request)
|
||||||
await market.reserveSlot(request.id, slotIndex)
|
await market.reserveSlot(request.id, slotIndex)
|
||||||
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateral)
|
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateralPerSlot)
|
||||||
let slotId = request.slotId(slotIndex)
|
let slotId = request.slotId(slotIndex)
|
||||||
let expected = Slot(request: request, slotIndex: slotIndex)
|
let expected = Slot(request: request, slotIndex: slotIndex)
|
||||||
check (await market.getActiveSlot(slotId)) == some expected
|
check (await market.getActiveSlot(slotId)) == some expected
|
||||||
@ -421,7 +431,7 @@ ethersuite "On-Chain Market":
|
|||||||
test "retrieves correct slot state once filled":
|
test "retrieves correct slot state once filled":
|
||||||
await market.requestStorage(request)
|
await market.requestStorage(request)
|
||||||
await market.reserveSlot(request.id, slotIndex)
|
await market.reserveSlot(request.id, slotIndex)
|
||||||
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateral)
|
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateralPerSlot)
|
||||||
let slotId = request.slotId(slotIndex)
|
let slotId = request.slotId(slotIndex)
|
||||||
check (await market.slotState(slotId)) == SlotState.Filled
|
check (await market.slotState(slotId)) == SlotState.Filled
|
||||||
|
|
||||||
@ -451,9 +461,9 @@ ethersuite "On-Chain Market":
|
|||||||
await market.reserveSlot(request.id, 0.u256)
|
await market.reserveSlot(request.id, 0.u256)
|
||||||
await market.reserveSlot(request.id, 1.u256)
|
await market.reserveSlot(request.id, 1.u256)
|
||||||
await market.reserveSlot(request.id, 2.u256)
|
await market.reserveSlot(request.id, 2.u256)
|
||||||
await market.fillSlot(request.id, 0.u256, proof, request.ask.collateral)
|
await market.fillSlot(request.id, 0.u256, proof, request.ask.collateralPerSlot)
|
||||||
await market.fillSlot(request.id, 1.u256, proof, request.ask.collateral)
|
await market.fillSlot(request.id, 1.u256, proof, request.ask.collateralPerSlot)
|
||||||
await market.fillSlot(request.id, 2.u256, proof, request.ask.collateral)
|
await market.fillSlot(request.id, 2.u256, proof, request.ask.collateralPerSlot)
|
||||||
let slotId = request.slotId(slotIndex)
|
let slotId = request.slotId(slotIndex)
|
||||||
|
|
||||||
# `market.fill` executes an `approve` tx before the `fillSlot` tx, so that's
|
# `market.fill` executes an `approve` tx before the `fillSlot` tx, so that's
|
||||||
@ -471,7 +481,7 @@ ethersuite "On-Chain Market":
|
|||||||
test "can query past SlotFilled events since given timestamp":
|
test "can query past SlotFilled events since given timestamp":
|
||||||
await market.requestStorage(request)
|
await market.requestStorage(request)
|
||||||
await market.reserveSlot(request.id, 0.u256)
|
await market.reserveSlot(request.id, 0.u256)
|
||||||
await market.fillSlot(request.id, 0.u256, proof, request.ask.collateral)
|
await market.fillSlot(request.id, 0.u256, proof, request.ask.collateralPerSlot)
|
||||||
|
|
||||||
# The SlotFilled event will be included in the same block as
|
# The SlotFilled event will be included in the same block as
|
||||||
# the fillSlot transaction. If we want to ignore the SlotFilled event
|
# the fillSlot transaction. If we want to ignore the SlotFilled event
|
||||||
@ -484,8 +494,8 @@ ethersuite "On-Chain Market":
|
|||||||
|
|
||||||
await market.reserveSlot(request.id, 1.u256)
|
await market.reserveSlot(request.id, 1.u256)
|
||||||
await market.reserveSlot(request.id, 2.u256)
|
await market.reserveSlot(request.id, 2.u256)
|
||||||
await market.fillSlot(request.id, 1.u256, proof, request.ask.collateral)
|
await market.fillSlot(request.id, 1.u256, proof, request.ask.collateralPerSlot)
|
||||||
await market.fillSlot(request.id, 2.u256, proof, request.ask.collateral)
|
await market.fillSlot(request.id, 2.u256, proof, request.ask.collateralPerSlot)
|
||||||
|
|
||||||
let events = await market.queryPastSlotFilledEvents(
|
let events = await market.queryPastSlotFilledEvents(
|
||||||
fromTime = fromTime.truncate(SecondsSince1970)
|
fromTime = fromTime.truncate(SecondsSince1970)
|
||||||
@ -503,9 +513,9 @@ ethersuite "On-Chain Market":
|
|||||||
await market.reserveSlot(request.id, 0.u256)
|
await market.reserveSlot(request.id, 0.u256)
|
||||||
await market.reserveSlot(request.id, 1.u256)
|
await market.reserveSlot(request.id, 1.u256)
|
||||||
await market.reserveSlot(request.id, 2.u256)
|
await market.reserveSlot(request.id, 2.u256)
|
||||||
await market.fillSlot(request.id, 0.u256, proof, request.ask.collateral)
|
await market.fillSlot(request.id, 0.u256, proof, request.ask.collateralPerSlot)
|
||||||
await market.fillSlot(request.id, 1.u256, proof, request.ask.collateral)
|
await market.fillSlot(request.id, 1.u256, proof, request.ask.collateralPerSlot)
|
||||||
await market.fillSlot(request.id, 2.u256, proof, request.ask.collateral)
|
await market.fillSlot(request.id, 2.u256, proof, request.ask.collateralPerSlot)
|
||||||
|
|
||||||
await ethProvider.advanceTime(10.u256)
|
await ethProvider.advanceTime(10.u256)
|
||||||
|
|
||||||
@ -531,12 +541,14 @@ ethersuite "On-Chain Market":
|
|||||||
let address = await host.getAddress()
|
let address = await host.getAddress()
|
||||||
switchAccount(host)
|
switchAccount(host)
|
||||||
await market.reserveSlot(request.id, 0.u256)
|
await market.reserveSlot(request.id, 0.u256)
|
||||||
await market.fillSlot(request.id, 0.u256, proof, request.ask.collateral)
|
await market.fillSlot(request.id, 0.u256, proof, request.ask.collateralPerSlot)
|
||||||
let filledAt = (await ethProvider.currentTime()) - 1.u256
|
let filledAt = (await ethProvider.currentTime()) - 1.u256
|
||||||
|
|
||||||
for slotIndex in 1 ..< request.ask.slots:
|
for slotIndex in 1 ..< request.ask.slots:
|
||||||
await market.reserveSlot(request.id, slotIndex.u256)
|
await market.reserveSlot(request.id, slotIndex.u256)
|
||||||
await market.fillSlot(request.id, slotIndex.u256, proof, request.ask.collateral)
|
await market.fillSlot(
|
||||||
|
request.id, slotIndex.u256, proof, request.ask.collateralPerSlot
|
||||||
|
)
|
||||||
|
|
||||||
let requestEnd = await market.getRequestEnd(request.id)
|
let requestEnd = await market.getRequestEnd(request.id)
|
||||||
await ethProvider.advanceTimeTo(requestEnd.u256 + 1)
|
await ethProvider.advanceTimeTo(requestEnd.u256 + 1)
|
||||||
@ -546,7 +558,7 @@ ethersuite "On-Chain Market":
|
|||||||
let endBalance = await token.balanceOf(address)
|
let endBalance = await token.balanceOf(address)
|
||||||
|
|
||||||
let expectedPayout = request.expectedPayout(filledAt, requestEnd.u256)
|
let expectedPayout = request.expectedPayout(filledAt, requestEnd.u256)
|
||||||
check endBalance == (startBalance + expectedPayout + request.ask.collateral)
|
check endBalance == (startBalance + expectedPayout + request.ask.collateralPerSlot)
|
||||||
|
|
||||||
test "pays rewards to reward recipient, collateral to host":
|
test "pays rewards to reward recipient, collateral to host":
|
||||||
market = OnChainMarket.new(marketplace, hostRewardRecipient.some)
|
market = OnChainMarket.new(marketplace, hostRewardRecipient.some)
|
||||||
@ -556,12 +568,14 @@ ethersuite "On-Chain Market":
|
|||||||
|
|
||||||
switchAccount(host)
|
switchAccount(host)
|
||||||
await market.reserveSlot(request.id, 0.u256)
|
await market.reserveSlot(request.id, 0.u256)
|
||||||
await market.fillSlot(request.id, 0.u256, proof, request.ask.collateral)
|
await market.fillSlot(request.id, 0.u256, proof, request.ask.collateralPerSlot)
|
||||||
let filledAt = (await ethProvider.currentTime()) - 1.u256
|
let filledAt = (await ethProvider.currentTime()) - 1.u256
|
||||||
|
|
||||||
for slotIndex in 1 ..< request.ask.slots:
|
for slotIndex in 1 ..< request.ask.slots:
|
||||||
await market.reserveSlot(request.id, slotIndex.u256)
|
await market.reserveSlot(request.id, slotIndex.u256)
|
||||||
await market.fillSlot(request.id, slotIndex.u256, proof, request.ask.collateral)
|
await market.fillSlot(
|
||||||
|
request.id, slotIndex.u256, proof, request.ask.collateralPerSlot
|
||||||
|
)
|
||||||
|
|
||||||
let requestEnd = await market.getRequestEnd(request.id)
|
let requestEnd = await market.getRequestEnd(request.id)
|
||||||
await ethProvider.advanceTimeTo(requestEnd.u256 + 1)
|
await ethProvider.advanceTimeTo(requestEnd.u256 + 1)
|
||||||
@ -575,5 +589,5 @@ ethersuite "On-Chain Market":
|
|||||||
let endBalanceReward = await token.balanceOf(hostRewardRecipient)
|
let endBalanceReward = await token.balanceOf(hostRewardRecipient)
|
||||||
|
|
||||||
let expectedPayout = request.expectedPayout(filledAt, requestEnd.u256)
|
let expectedPayout = request.expectedPayout(filledAt, requestEnd.u256)
|
||||||
check endBalanceHost == (startBalanceHost + request.ask.collateral)
|
check endBalanceHost == (startBalanceHost + request.ask.collateralPerSlot)
|
||||||
check endBalanceReward == (startBalanceReward + expectedPayout)
|
check endBalanceReward == (startBalanceReward + expectedPayout)
|
||||||
|
|||||||
@ -51,9 +51,9 @@ proc example*(_: type StorageRequest): StorageRequest =
|
|||||||
slots: 4,
|
slots: 4,
|
||||||
slotSize: (1 * 1024 * 1024 * 1024).u256, # 1 Gigabyte
|
slotSize: (1 * 1024 * 1024 * 1024).u256, # 1 Gigabyte
|
||||||
duration: (10 * 60 * 60).u256, # 10 hours
|
duration: (10 * 60 * 60).u256, # 10 hours
|
||||||
collateral: 200.u256,
|
collateralPerByte: 1.u256,
|
||||||
proofProbability: 4.u256, # require a proof roughly once every 4 periods
|
proofProbability: 4.u256, # require a proof roughly once every 4 periods
|
||||||
reward: 84.u256,
|
pricePerBytePerSecond: 1.u256,
|
||||||
maxSlotLoss: 2, # 2 slots can be freed without data considered to be lost
|
maxSlotLoss: 2, # 2 slots can be freed without data considered to be lost
|
||||||
),
|
),
|
||||||
content: StorageContent(
|
content: StorageContent(
|
||||||
|
|||||||
@ -105,9 +105,9 @@ proc requestStorageRaw*(
|
|||||||
client: CodexClient,
|
client: CodexClient,
|
||||||
cid: Cid,
|
cid: Cid,
|
||||||
duration: UInt256,
|
duration: UInt256,
|
||||||
reward: UInt256,
|
pricePerBytePerSecond: UInt256,
|
||||||
proofProbability: UInt256,
|
proofProbability: UInt256,
|
||||||
collateral: UInt256,
|
collateralPerByte: UInt256,
|
||||||
expiry: uint = 0,
|
expiry: uint = 0,
|
||||||
nodes: uint = 3,
|
nodes: uint = 3,
|
||||||
tolerance: uint = 1,
|
tolerance: uint = 1,
|
||||||
@ -118,9 +118,9 @@ proc requestStorageRaw*(
|
|||||||
let json =
|
let json =
|
||||||
%*{
|
%*{
|
||||||
"duration": duration,
|
"duration": duration,
|
||||||
"reward": reward,
|
"pricePerBytePerSecond": pricePerBytePerSecond,
|
||||||
"proofProbability": proofProbability,
|
"proofProbability": proofProbability,
|
||||||
"collateral": collateral,
|
"collateralPerByte": collateralPerByte,
|
||||||
"nodes": nodes,
|
"nodes": nodes,
|
||||||
"tolerance": tolerance,
|
"tolerance": tolerance,
|
||||||
}
|
}
|
||||||
@ -134,17 +134,18 @@ proc requestStorage*(
|
|||||||
client: CodexClient,
|
client: CodexClient,
|
||||||
cid: Cid,
|
cid: Cid,
|
||||||
duration: UInt256,
|
duration: UInt256,
|
||||||
reward: UInt256,
|
pricePerBytePerSecond: UInt256,
|
||||||
proofProbability: UInt256,
|
proofProbability: UInt256,
|
||||||
expiry: uint,
|
expiry: uint,
|
||||||
collateral: UInt256,
|
collateralPerByte: UInt256,
|
||||||
nodes: uint = 3,
|
nodes: uint = 3,
|
||||||
tolerance: uint = 1,
|
tolerance: uint = 1,
|
||||||
): ?!PurchaseId =
|
): ?!PurchaseId =
|
||||||
## Call request storage REST endpoint
|
## Call request storage REST endpoint
|
||||||
##
|
##
|
||||||
let response = client.requestStorageRaw(
|
let response = client.requestStorageRaw(
|
||||||
cid, duration, reward, proofProbability, collateral, expiry, nodes, tolerance
|
cid, duration, pricePerBytePerSecond, proofProbability, collateralPerByte, expiry,
|
||||||
|
nodes, tolerance,
|
||||||
)
|
)
|
||||||
if response.status != "200 OK":
|
if response.status != "200 OK":
|
||||||
doAssert(false, response.body)
|
doAssert(false, response.body)
|
||||||
@ -172,7 +173,8 @@ proc getSlots*(client: CodexClient): ?!seq[Slot] =
|
|||||||
seq[Slot].fromJson(body)
|
seq[Slot].fromJson(body)
|
||||||
|
|
||||||
proc postAvailability*(
|
proc postAvailability*(
|
||||||
client: CodexClient, totalSize, duration, minPrice, maxCollateral: UInt256
|
client: CodexClient,
|
||||||
|
totalSize, duration, minPricePerBytePerSecond, totalCollateral: UInt256,
|
||||||
): ?!Availability =
|
): ?!Availability =
|
||||||
## Post sales availability endpoint
|
## Post sales availability endpoint
|
||||||
##
|
##
|
||||||
@ -181,8 +183,8 @@ proc postAvailability*(
|
|||||||
%*{
|
%*{
|
||||||
"totalSize": totalSize,
|
"totalSize": totalSize,
|
||||||
"duration": duration,
|
"duration": duration,
|
||||||
"minPrice": minPrice,
|
"minPricePerBytePerSecond": minPricePerBytePerSecond,
|
||||||
"maxCollateral": maxCollateral,
|
"totalCollateral": totalCollateral,
|
||||||
}
|
}
|
||||||
let response = client.http.post(url, $json)
|
let response = client.http.post(url, $json)
|
||||||
doAssert response.status == "201 Created",
|
doAssert response.status == "201 Created",
|
||||||
@ -192,7 +194,8 @@ proc postAvailability*(
|
|||||||
proc patchAvailabilityRaw*(
|
proc patchAvailabilityRaw*(
|
||||||
client: CodexClient,
|
client: CodexClient,
|
||||||
availabilityId: AvailabilityId,
|
availabilityId: AvailabilityId,
|
||||||
totalSize, freeSize, duration, minPrice, maxCollateral: ?UInt256 = UInt256.none,
|
totalSize, freeSize, duration, minPricePerBytePerSecond, totalCollateral: ?UInt256 =
|
||||||
|
UInt256.none,
|
||||||
): Response =
|
): Response =
|
||||||
## Updates availability
|
## Updates availability
|
||||||
##
|
##
|
||||||
@ -210,25 +213,26 @@ proc patchAvailabilityRaw*(
|
|||||||
if duration =? duration:
|
if duration =? duration:
|
||||||
json["duration"] = %duration
|
json["duration"] = %duration
|
||||||
|
|
||||||
if minPrice =? minPrice:
|
if minPricePerBytePerSecond =? minPricePerBytePerSecond:
|
||||||
json["minPrice"] = %minPrice
|
json["minPricePerBytePerSecond"] = %minPricePerBytePerSecond
|
||||||
|
|
||||||
if maxCollateral =? maxCollateral:
|
if totalCollateral =? totalCollateral:
|
||||||
json["maxCollateral"] = %maxCollateral
|
json["totalCollateral"] = %totalCollateral
|
||||||
|
|
||||||
client.http.patch(url, $json)
|
client.http.patch(url, $json)
|
||||||
|
|
||||||
proc patchAvailability*(
|
proc patchAvailability*(
|
||||||
client: CodexClient,
|
client: CodexClient,
|
||||||
availabilityId: AvailabilityId,
|
availabilityId: AvailabilityId,
|
||||||
totalSize, duration, minPrice, maxCollateral: ?UInt256 = UInt256.none,
|
totalSize, duration, minPricePerBytePerSecond, totalCollateral: ?UInt256 =
|
||||||
|
UInt256.none,
|
||||||
): void =
|
): void =
|
||||||
let response = client.patchAvailabilityRaw(
|
let response = client.patchAvailabilityRaw(
|
||||||
availabilityId,
|
availabilityId,
|
||||||
totalSize = totalSize,
|
totalSize = totalSize,
|
||||||
duration = duration,
|
duration = duration,
|
||||||
minPrice = minPrice,
|
minPricePerBytePerSecond = minPricePerBytePerSecond,
|
||||||
maxCollateral = maxCollateral,
|
totalCollateral = totalCollateral,
|
||||||
)
|
)
|
||||||
doAssert response.status == "200 OK", "expected 200 OK, got " & response.status
|
doAssert response.status == "200 OK", "expected 200 OK, got " & response.status
|
||||||
|
|
||||||
|
|||||||
@ -45,16 +45,28 @@ template marketplacesuite*(name: string, body: untyped) =
|
|||||||
proc periods(p: int): uint64 =
|
proc periods(p: int): uint64 =
|
||||||
p.uint64 * period
|
p.uint64 * period
|
||||||
|
|
||||||
proc createAvailabilities(datasetSize: int, duration: uint64) =
|
proc slotSize(blocks: int): UInt256 =
|
||||||
|
(DefaultBlockSize * blocks.NBytes).Natural.u256
|
||||||
|
|
||||||
|
proc datasetSize(blocks, nodes, tolerance: int): UInt256 =
|
||||||
|
(nodes + tolerance).u256 * slotSize(blocks)
|
||||||
|
|
||||||
|
proc createAvailabilities(
|
||||||
|
datasetSize: UInt256,
|
||||||
|
duration: uint64,
|
||||||
|
collateralPerByte: UInt256,
|
||||||
|
minPricePerBytePerSecond: UInt256,
|
||||||
|
) =
|
||||||
|
let totalCollateral = datasetSize * collateralPerByte
|
||||||
# post availability to each provider
|
# post availability to each provider
|
||||||
for i in 0 ..< providers().len:
|
for i in 0 ..< providers().len:
|
||||||
let provider = providers()[i].client
|
let provider = providers()[i].client
|
||||||
|
|
||||||
discard provider.postAvailability(
|
discard provider.postAvailability(
|
||||||
totalSize = datasetSize.u256, # should match 1 slot only
|
totalSize = datasetSize,
|
||||||
duration = duration.u256,
|
duration = duration.u256,
|
||||||
minPrice = 300.u256,
|
minPricePerBytePerSecond = minPricePerBytePerSecond,
|
||||||
maxCollateral = 200.u256,
|
totalCollateral = totalCollateral,
|
||||||
)
|
)
|
||||||
|
|
||||||
proc requestStorage(
|
proc requestStorage(
|
||||||
@ -62,8 +74,8 @@ template marketplacesuite*(name: string, body: untyped) =
|
|||||||
cid: Cid,
|
cid: Cid,
|
||||||
proofProbability = 1,
|
proofProbability = 1,
|
||||||
duration: uint64 = 12.periods,
|
duration: uint64 = 12.periods,
|
||||||
reward = 400.u256,
|
pricePerBytePerSecond = 1.u256,
|
||||||
collateral = 100.u256,
|
collateralPerByte = 1.u256,
|
||||||
expiry: uint64 = 4.periods,
|
expiry: uint64 = 4.periods,
|
||||||
nodes = providers().len,
|
nodes = providers().len,
|
||||||
tolerance = 0,
|
tolerance = 0,
|
||||||
@ -73,8 +85,8 @@ template marketplacesuite*(name: string, body: untyped) =
|
|||||||
expiry = expiry.uint,
|
expiry = expiry.uint,
|
||||||
duration = duration.u256,
|
duration = duration.u256,
|
||||||
proofProbability = proofProbability.u256,
|
proofProbability = proofProbability.u256,
|
||||||
collateral = collateral,
|
collateralPerByte = collateralPerByte,
|
||||||
reward = reward,
|
pricePerBytePerSecond = pricePerBytePerSecond,
|
||||||
nodes = nodes.uint,
|
nodes = nodes.uint,
|
||||||
tolerance = tolerance.uint,
|
tolerance = tolerance.uint,
|
||||||
).get
|
).get
|
||||||
|
|||||||
@ -19,9 +19,9 @@ marketplacesuite "Bug #821 - node crashes during erasure coding":
|
|||||||
# .withLogTopics("node", "marketplace", "sales", "reservations", "node", "proving", "clock")
|
# .withLogTopics("node", "marketplace", "sales", "reservations", "node", "proving", "clock")
|
||||||
.some,
|
.some,
|
||||||
):
|
):
|
||||||
let reward = 400.u256
|
let pricePerBytePerSecond = 1.u256
|
||||||
let duration = 20.periods
|
let duration = 20.periods
|
||||||
let collateral = 200.u256
|
let collateralPerByte = 1.u256
|
||||||
let expiry = 10.periods
|
let expiry = 10.periods
|
||||||
let data = await RandomChunker.example(blocks = 8)
|
let data = await RandomChunker.example(blocks = 8)
|
||||||
let client = clients()[0]
|
let client = clients()[0]
|
||||||
@ -40,9 +40,9 @@ marketplacesuite "Bug #821 - node crashes during erasure coding":
|
|||||||
let id = await clientApi.requestStorage(
|
let id = await clientApi.requestStorage(
|
||||||
cid,
|
cid,
|
||||||
duration = duration,
|
duration = duration,
|
||||||
reward = reward,
|
pricePerBytePerSecond = pricePerBytePerSecond,
|
||||||
expiry = expiry,
|
expiry = expiry,
|
||||||
collateral = collateral,
|
collateralPerByte = collateralPerByte,
|
||||||
nodes = 3,
|
nodes = 3,
|
||||||
tolerance = 1,
|
tolerance = 1,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -16,6 +16,12 @@ marketplacesuite "Marketplace":
|
|||||||
var client: CodexClient
|
var client: CodexClient
|
||||||
var clientAccount: Address
|
var clientAccount: Address
|
||||||
|
|
||||||
|
const minPricePerBytePerSecond = 1.u256
|
||||||
|
const collateralPerByte = 1.u256
|
||||||
|
const blocks = 8
|
||||||
|
const ecNodes = 3
|
||||||
|
const ecTolerance = 1
|
||||||
|
|
||||||
setup:
|
setup:
|
||||||
host = providers()[0].client
|
host = providers()[0].client
|
||||||
hostAccount = providers()[0].ethAccount
|
hostAccount = providers()[0].ethAccount
|
||||||
@ -29,13 +35,13 @@ marketplacesuite "Marketplace":
|
|||||||
|
|
||||||
test "nodes negotiate contracts on the marketplace", marketplaceConfig:
|
test "nodes negotiate contracts on the marketplace", marketplaceConfig:
|
||||||
let size = 0xFFFFFF.u256
|
let size = 0xFFFFFF.u256
|
||||||
let data = await RandomChunker.example(blocks = 8)
|
let data = await RandomChunker.example(blocks = blocks)
|
||||||
# host makes storage available
|
# host makes storage available
|
||||||
let availability = host.postAvailability(
|
let availability = host.postAvailability(
|
||||||
totalSize = size,
|
totalSize = size,
|
||||||
duration = 20 * 60.u256,
|
duration = 20 * 60.u256,
|
||||||
minPrice = 300.u256,
|
minPricePerBytePerSecond = minPricePerBytePerSecond,
|
||||||
maxCollateral = 300.u256,
|
totalCollateral = size * minPricePerBytePerSecond,
|
||||||
).get
|
).get
|
||||||
|
|
||||||
# client requests storage
|
# client requests storage
|
||||||
@ -43,12 +49,12 @@ marketplacesuite "Marketplace":
|
|||||||
let id = client.requestStorage(
|
let id = client.requestStorage(
|
||||||
cid,
|
cid,
|
||||||
duration = 20 * 60.u256,
|
duration = 20 * 60.u256,
|
||||||
reward = 400.u256,
|
pricePerBytePerSecond = minPricePerBytePerSecond,
|
||||||
proofProbability = 3.u256,
|
proofProbability = 3.u256,
|
||||||
expiry = 10 * 60,
|
expiry = 10 * 60,
|
||||||
collateral = 200.u256,
|
collateralPerByte = collateralPerByte,
|
||||||
nodes = 3,
|
nodes = ecNodes,
|
||||||
tolerance = 1,
|
tolerance = ecTolerance,
|
||||||
).get
|
).get
|
||||||
|
|
||||||
check eventually(client.purchaseStateIs(id, "started"), timeout = 10 * 60 * 1000)
|
check eventually(client.purchaseStateIs(id, "started"), timeout = 10 * 60 * 1000)
|
||||||
@ -66,21 +72,19 @@ marketplacesuite "Marketplace":
|
|||||||
test "node slots gets paid out and rest of tokens are returned to client",
|
test "node slots gets paid out and rest of tokens are returned to client",
|
||||||
marketplaceConfig:
|
marketplaceConfig:
|
||||||
let size = 0xFFFFFF.u256
|
let size = 0xFFFFFF.u256
|
||||||
let data = await RandomChunker.example(blocks = 8)
|
let data = await RandomChunker.example(blocks = blocks)
|
||||||
let marketplace = Marketplace.new(Marketplace.address, ethProvider.getSigner())
|
let marketplace = Marketplace.new(Marketplace.address, ethProvider.getSigner())
|
||||||
let tokenAddress = await marketplace.token()
|
let tokenAddress = await marketplace.token()
|
||||||
let token = Erc20Token.new(tokenAddress, ethProvider.getSigner())
|
let token = Erc20Token.new(tokenAddress, ethProvider.getSigner())
|
||||||
let reward = 400.u256
|
|
||||||
let duration = 20 * 60.u256
|
let duration = 20 * 60.u256
|
||||||
let nodes = 3'u
|
|
||||||
|
|
||||||
# host makes storage available
|
# host makes storage available
|
||||||
let startBalanceHost = await token.balanceOf(hostAccount)
|
let startBalanceHost = await token.balanceOf(hostAccount)
|
||||||
discard host.postAvailability(
|
discard host.postAvailability(
|
||||||
totalSize = size,
|
totalSize = size,
|
||||||
duration = 20 * 60.u256,
|
duration = 20 * 60.u256,
|
||||||
minPrice = 300.u256,
|
minPricePerBytePerSecond = minPricePerBytePerSecond,
|
||||||
maxCollateral = 300.u256,
|
totalCollateral = size * minPricePerBytePerSecond,
|
||||||
).get
|
).get
|
||||||
|
|
||||||
# client requests storage
|
# client requests storage
|
||||||
@ -88,12 +92,12 @@ marketplacesuite "Marketplace":
|
|||||||
let id = client.requestStorage(
|
let id = client.requestStorage(
|
||||||
cid,
|
cid,
|
||||||
duration = duration,
|
duration = duration,
|
||||||
reward = reward,
|
pricePerBytePerSecond = minPricePerBytePerSecond,
|
||||||
proofProbability = 3.u256,
|
proofProbability = 3.u256,
|
||||||
expiry = 10 * 60,
|
expiry = 10 * 60,
|
||||||
collateral = 200.u256,
|
collateralPerByte = collateralPerByte,
|
||||||
nodes = nodes,
|
nodes = ecNodes,
|
||||||
tolerance = 1,
|
tolerance = ecTolerance,
|
||||||
).get
|
).get
|
||||||
|
|
||||||
check eventually(client.purchaseStateIs(id, "started"), timeout = 10 * 60 * 1000)
|
check eventually(client.purchaseStateIs(id, "started"), timeout = 10 * 60 * 1000)
|
||||||
@ -108,8 +112,10 @@ marketplacesuite "Marketplace":
|
|||||||
await ethProvider.advanceTime(duration)
|
await ethProvider.advanceTime(duration)
|
||||||
|
|
||||||
# Checking that the hosting node received reward for at least the time between <expiry;end>
|
# Checking that the hosting node received reward for at least the time between <expiry;end>
|
||||||
|
let slotSize = slotSize(blocks)
|
||||||
|
let pricePerSlotPerSecond = minPricePerBytePerSecond * slotSize
|
||||||
check eventually (await token.balanceOf(hostAccount)) - startBalanceHost >=
|
check eventually (await token.balanceOf(hostAccount)) - startBalanceHost >=
|
||||||
(duration - 5 * 60) * reward * nodes.u256
|
(duration - 5 * 60) * pricePerSlotPerSecond * ecNodes.u256
|
||||||
|
|
||||||
# Checking that client node receives some funds back that were not used for the host nodes
|
# Checking that client node receives some funds back that were not used for the host nodes
|
||||||
check eventually(
|
check eventually(
|
||||||
@ -118,6 +124,12 @@ marketplacesuite "Marketplace":
|
|||||||
)
|
)
|
||||||
|
|
||||||
marketplacesuite "Marketplace payouts":
|
marketplacesuite "Marketplace payouts":
|
||||||
|
const minPricePerBytePerSecond = 1.u256
|
||||||
|
const collateralPerByte = 1.u256
|
||||||
|
const blocks = 8
|
||||||
|
const ecNodes = 3
|
||||||
|
const ecTolerance = 1
|
||||||
|
|
||||||
test "expired request partially pays out for stored time",
|
test "expired request partially pays out for stored time",
|
||||||
NodeConfigs(
|
NodeConfigs(
|
||||||
# Uncomment to start Hardhat automatically, typically so logs can be inspected locally
|
# Uncomment to start Hardhat automatically, typically so logs can be inspected locally
|
||||||
@ -133,11 +145,9 @@ marketplacesuite "Marketplace payouts":
|
|||||||
# .withLogTopics("node", "marketplace", "sales", "reservations", "node", "proving", "clock")
|
# .withLogTopics("node", "marketplace", "sales", "reservations", "node", "proving", "clock")
|
||||||
.some,
|
.some,
|
||||||
):
|
):
|
||||||
let reward = 400.u256
|
|
||||||
let duration = 20.periods
|
let duration = 20.periods
|
||||||
let collateral = 200.u256
|
|
||||||
let expiry = 10.periods
|
let expiry = 10.periods
|
||||||
let data = await RandomChunker.example(blocks = 8)
|
let data = await RandomChunker.example(blocks = blocks)
|
||||||
let client = clients()[0]
|
let client = clients()[0]
|
||||||
let provider = providers()[0]
|
let provider = providers()[0]
|
||||||
let clientApi = client.client
|
let clientApi = client.client
|
||||||
@ -146,13 +156,15 @@ marketplacesuite "Marketplace payouts":
|
|||||||
let startBalanceClient = await token.balanceOf(client.ethAccount)
|
let startBalanceClient = await token.balanceOf(client.ethAccount)
|
||||||
|
|
||||||
# provider makes storage available
|
# provider makes storage available
|
||||||
|
let datasetSize = datasetSize(blocks, ecNodes, ecTolerance)
|
||||||
|
let totalAvailabilitySize = datasetSize div 2
|
||||||
discard providerApi.postAvailability(
|
discard providerApi.postAvailability(
|
||||||
# make availability size small enough that we can't fill all the slots,
|
# make availability size small enough that we can't fill all the slots,
|
||||||
# thus causing a cancellation
|
# thus causing a cancellation
|
||||||
totalSize = (data.len div 2).u256,
|
totalSize = totalAvailabilitySize,
|
||||||
duration = duration.u256,
|
duration = duration.u256,
|
||||||
minPrice = reward,
|
minPricePerBytePerSecond = minPricePerBytePerSecond,
|
||||||
maxCollateral = collateral,
|
totalCollateral = collateralPerByte * totalAvailabilitySize,
|
||||||
)
|
)
|
||||||
|
|
||||||
let cid = clientApi.upload(data).get
|
let cid = clientApi.upload(data).get
|
||||||
@ -168,11 +180,11 @@ marketplacesuite "Marketplace payouts":
|
|||||||
let id = await clientApi.requestStorage(
|
let id = await clientApi.requestStorage(
|
||||||
cid,
|
cid,
|
||||||
duration = duration,
|
duration = duration,
|
||||||
reward = reward,
|
pricePerBytePerSecond = minPricePerBytePerSecond,
|
||||||
expiry = expiry,
|
expiry = expiry,
|
||||||
collateral = collateral,
|
collateralPerByte = collateralPerByte,
|
||||||
nodes = 3,
|
nodes = ecNodes,
|
||||||
tolerance = 1,
|
tolerance = ecTolerance,
|
||||||
)
|
)
|
||||||
|
|
||||||
# wait until one slot is filled
|
# wait until one slot is filled
|
||||||
@ -185,10 +197,13 @@ marketplacesuite "Marketplace payouts":
|
|||||||
|
|
||||||
await advanceToNextPeriod()
|
await advanceToNextPeriod()
|
||||||
|
|
||||||
|
let slotSize = slotSize(blocks)
|
||||||
|
let pricePerSlotPerSecond = minPricePerBytePerSecond * slotSize
|
||||||
|
|
||||||
check eventually (
|
check eventually (
|
||||||
let endBalanceProvider = (await token.balanceOf(provider.ethAccount))
|
let endBalanceProvider = (await token.balanceOf(provider.ethAccount))
|
||||||
endBalanceProvider > startBalanceProvider and
|
endBalanceProvider > startBalanceProvider and
|
||||||
endBalanceProvider < startBalanceProvider + expiry.u256 * reward
|
endBalanceProvider < startBalanceProvider + expiry.u256 * pricePerSlotPerSecond
|
||||||
)
|
)
|
||||||
check eventually(
|
check eventually(
|
||||||
(
|
(
|
||||||
|
|||||||
@ -15,6 +15,12 @@ logScope:
|
|||||||
topics = "integration test proofs"
|
topics = "integration test proofs"
|
||||||
|
|
||||||
marketplacesuite "Hosts submit regular proofs":
|
marketplacesuite "Hosts submit regular proofs":
|
||||||
|
const minPricePerBytePerSecond = 1.u256
|
||||||
|
const collateralPerByte = 1.u256
|
||||||
|
const blocks = 8
|
||||||
|
const ecNodes = 3
|
||||||
|
const ecTolerance = 1
|
||||||
|
|
||||||
test "hosts submit periodic proofs for slots they fill",
|
test "hosts submit periodic proofs for slots they fill",
|
||||||
NodeConfigs(
|
NodeConfigs(
|
||||||
# Uncomment to start Hardhat automatically, typically so logs can be inspected locally
|
# Uncomment to start Hardhat automatically, typically so logs can be inspected locally
|
||||||
@ -34,14 +40,29 @@ marketplacesuite "Hosts submit regular proofs":
|
|||||||
let expiry = 10.periods
|
let expiry = 10.periods
|
||||||
let duration = expiry + 5.periods
|
let duration = expiry + 5.periods
|
||||||
|
|
||||||
let data = await RandomChunker.example(blocks = 8)
|
let data = await RandomChunker.example(blocks = blocks)
|
||||||
createAvailabilities(data.len * 2, duration) # TODO: better value for data.len
|
let datasetSize =
|
||||||
|
datasetSize(blocks = blocks, nodes = ecNodes, tolerance = ecTolerance)
|
||||||
|
createAvailabilities(
|
||||||
|
datasetSize, duration, collateralPerByte, minPricePerBytePerSecond
|
||||||
|
)
|
||||||
|
|
||||||
let cid = client0.upload(data).get
|
let cid = client0.upload(data).get
|
||||||
|
|
||||||
let purchaseId = await client0.requestStorage(
|
let purchaseId = await client0.requestStorage(
|
||||||
cid, expiry = expiry, duration = duration, nodes = 3, tolerance = 1
|
cid,
|
||||||
|
expiry = expiry,
|
||||||
|
duration = duration,
|
||||||
|
nodes = ecNodes,
|
||||||
|
tolerance = ecTolerance,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
let purchase = client0.getPurchase(purchaseId).get
|
||||||
|
check purchase.error == none string
|
||||||
|
|
||||||
|
let request = purchase.request.get
|
||||||
|
let slotSize = request.ask.slotSize
|
||||||
|
|
||||||
check eventually(
|
check eventually(
|
||||||
client0.purchaseStateIs(purchaseId, "started"), timeout = expiry.int * 1000
|
client0.purchaseStateIs(purchaseId, "started"), timeout = expiry.int * 1000
|
||||||
)
|
)
|
||||||
@ -62,6 +83,12 @@ marketplacesuite "Simulate invalid proofs":
|
|||||||
# tightened so that they are showing, as an integration test, that specific
|
# tightened so that they are showing, as an integration test, that specific
|
||||||
# proofs are being marked as missed by the validator.
|
# proofs are being marked as missed by the validator.
|
||||||
|
|
||||||
|
const minPricePerBytePerSecond = 1.u256
|
||||||
|
const collateralPerByte = 1.u256
|
||||||
|
const blocks = 8
|
||||||
|
const ecNodes = 3
|
||||||
|
const ecTolerance = 1
|
||||||
|
|
||||||
test "slot is freed after too many invalid proofs submitted",
|
test "slot is freed after too many invalid proofs submitted",
|
||||||
NodeConfigs(
|
NodeConfigs(
|
||||||
# Uncomment to start Hardhat automatically, typically so logs can be inspected locally
|
# Uncomment to start Hardhat automatically, typically so logs can be inspected locally
|
||||||
@ -88,8 +115,12 @@ marketplacesuite "Simulate invalid proofs":
|
|||||||
let expiry = 10.periods
|
let expiry = 10.periods
|
||||||
let duration = expiry + 10.periods
|
let duration = expiry + 10.periods
|
||||||
|
|
||||||
let data = await RandomChunker.example(blocks = 8)
|
let data = await RandomChunker.example(blocks = blocks)
|
||||||
createAvailabilities(data.len * 2, duration) # TODO: better value for data.len
|
let datasetSize =
|
||||||
|
datasetSize(blocks = blocks, nodes = ecNodes, tolerance = ecTolerance)
|
||||||
|
createAvailabilities(
|
||||||
|
datasetSize, duration, collateralPerByte, minPricePerBytePerSecond
|
||||||
|
)
|
||||||
|
|
||||||
let cid = client0.upload(data).get
|
let cid = client0.upload(data).get
|
||||||
|
|
||||||
@ -97,8 +128,8 @@ marketplacesuite "Simulate invalid proofs":
|
|||||||
cid,
|
cid,
|
||||||
expiry = expiry,
|
expiry = expiry,
|
||||||
duration = duration,
|
duration = duration,
|
||||||
nodes = 3,
|
nodes = ecNodes,
|
||||||
tolerance = 1,
|
tolerance = ecTolerance,
|
||||||
proofProbability = 1,
|
proofProbability = 1,
|
||||||
)
|
)
|
||||||
let requestId = client0.requestId(purchaseId).get
|
let requestId = client0.requestId(purchaseId).get
|
||||||
@ -144,8 +175,12 @@ marketplacesuite "Simulate invalid proofs":
|
|||||||
let expiry = 10.periods
|
let expiry = 10.periods
|
||||||
let duration = expiry + 10.periods
|
let duration = expiry + 10.periods
|
||||||
|
|
||||||
let data = await RandomChunker.example(blocks = 8)
|
let data = await RandomChunker.example(blocks = blocks)
|
||||||
createAvailabilities(data.len * 2, duration) # TODO: better value for data.len
|
let datasetSize =
|
||||||
|
datasetSize(blocks = blocks, nodes = ecNodes, tolerance = ecTolerance)
|
||||||
|
createAvailabilities(
|
||||||
|
datasetSize, duration, collateralPerByte, minPricePerBytePerSecond
|
||||||
|
)
|
||||||
|
|
||||||
let cid = client0.upload(data).get
|
let cid = client0.upload(data).get
|
||||||
|
|
||||||
@ -153,8 +188,8 @@ marketplacesuite "Simulate invalid proofs":
|
|||||||
cid,
|
cid,
|
||||||
expiry = expiry,
|
expiry = expiry,
|
||||||
duration = duration,
|
duration = duration,
|
||||||
nodes = 3,
|
nodes = ecNodes,
|
||||||
tolerance = 1,
|
tolerance = ecTolerance,
|
||||||
proofProbability = 1,
|
proofProbability = 1,
|
||||||
)
|
)
|
||||||
let requestId = client0.requestId(purchaseId).get
|
let requestId = client0.requestId(purchaseId).get
|
||||||
@ -187,6 +222,9 @@ marketplacesuite "Simulate invalid proofs":
|
|||||||
await freedSubscription.unsubscribe()
|
await freedSubscription.unsubscribe()
|
||||||
|
|
||||||
# TODO: uncomment once fixed
|
# TODO: uncomment once fixed
|
||||||
|
# WARNING: in the meantime minPrice has changed to minPricePerBytePerSecond
|
||||||
|
# and maxCollateral has changed to totalCollateral - double check if
|
||||||
|
# it is set correctly below
|
||||||
# test "host that submits invalid proofs is paid out less", NodeConfigs(
|
# test "host that submits invalid proofs is paid out less", NodeConfigs(
|
||||||
# # Uncomment to start Hardhat automatically, typically so logs can be inspected locally
|
# # Uncomment to start Hardhat automatically, typically so logs can be inspected locally
|
||||||
# # hardhat: HardhatConfig().withLogFile(),
|
# # hardhat: HardhatConfig().withLogFile(),
|
||||||
@ -227,8 +265,8 @@ marketplacesuite "Simulate invalid proofs":
|
|||||||
# discard provider0.client.postAvailability(
|
# discard provider0.client.postAvailability(
|
||||||
# totalSize=slotSize, # should match 1 slot only
|
# totalSize=slotSize, # should match 1 slot only
|
||||||
# duration=totalPeriods.periods.u256,
|
# duration=totalPeriods.periods.u256,
|
||||||
# minPrice=300.u256,
|
# minPricePerBytePerSecond=minPricePerBytePerSecond,
|
||||||
# maxCollateral=200.u256
|
# totalCollateral=slotSize * minPricePerBytePerSecond
|
||||||
# )
|
# )
|
||||||
|
|
||||||
# let cid = client0.upload(data).get
|
# let cid = client0.upload(data).get
|
||||||
@ -260,8 +298,8 @@ marketplacesuite "Simulate invalid proofs":
|
|||||||
# discard provider1.client.postAvailability(
|
# discard provider1.client.postAvailability(
|
||||||
# totalSize=slotSize, # should match 1 slot only
|
# totalSize=slotSize, # should match 1 slot only
|
||||||
# duration=totalPeriods.periods.u256,
|
# duration=totalPeriods.periods.u256,
|
||||||
# minPrice=300.u256,
|
# minPricePerBytePerSecond=minPricePerBytePerSecond,
|
||||||
# maxCollateral=200.u256
|
# totalCollateral=slotSize * minPricePerBytePerSecond
|
||||||
# )
|
# )
|
||||||
|
|
||||||
# check eventually filledSlotIds.len > 1
|
# check eventually filledSlotIds.len > 1
|
||||||
@ -269,8 +307,8 @@ marketplacesuite "Simulate invalid proofs":
|
|||||||
# discard provider2.client.postAvailability(
|
# discard provider2.client.postAvailability(
|
||||||
# totalSize=slotSize, # should match 1 slot only
|
# totalSize=slotSize, # should match 1 slot only
|
||||||
# duration=totalPeriods.periods.u256,
|
# duration=totalPeriods.periods.u256,
|
||||||
# minPrice=300.u256,
|
# minPricePerBytePerSecond=minPricePerBytePerSecond,
|
||||||
# maxCollateral=200.u256
|
# totalCollateral=slotSize * minPricePerBytePerSecond
|
||||||
# )
|
# )
|
||||||
|
|
||||||
# check eventually filledSlotIds.len > 2
|
# check eventually filledSlotIds.len > 2
|
||||||
|
|||||||
@ -12,18 +12,18 @@ twonodessuite "Purchasing":
|
|||||||
let id1 = client1.requestStorage(
|
let id1 = client1.requestStorage(
|
||||||
cid,
|
cid,
|
||||||
duration = 100.u256,
|
duration = 100.u256,
|
||||||
reward = 2.u256,
|
pricePerBytePerSecond = 1.u256,
|
||||||
proofProbability = 3.u256,
|
proofProbability = 3.u256,
|
||||||
expiry = 10,
|
expiry = 10,
|
||||||
collateral = 200.u256,
|
collateralPerByte = 1.u256,
|
||||||
).get
|
).get
|
||||||
let id2 = client1.requestStorage(
|
let id2 = client1.requestStorage(
|
||||||
cid,
|
cid,
|
||||||
duration = 400.u256,
|
duration = 400.u256,
|
||||||
reward = 5.u256,
|
pricePerBytePerSecond = 2.u256,
|
||||||
proofProbability = 6.u256,
|
proofProbability = 6.u256,
|
||||||
expiry = 10,
|
expiry = 10,
|
||||||
collateral = 201.u256,
|
collateralPerByte = 2.u256,
|
||||||
).get
|
).get
|
||||||
check id1 != id2
|
check id1 != id2
|
||||||
|
|
||||||
@ -38,33 +38,36 @@ twonodessuite "Purchasing":
|
|||||||
let id = client1.requestStorage(
|
let id = client1.requestStorage(
|
||||||
cid,
|
cid,
|
||||||
duration = 100.u256,
|
duration = 100.u256,
|
||||||
reward = 2.u256,
|
pricePerBytePerSecond = 1.u256,
|
||||||
proofProbability = 3.u256,
|
proofProbability = 3.u256,
|
||||||
expiry = 30,
|
expiry = 30,
|
||||||
collateral = 200.u256,
|
collateralPerByte = 1.u256,
|
||||||
nodes = 3,
|
nodes = 3,
|
||||||
tolerance = 1,
|
tolerance = 1,
|
||||||
).get
|
).get
|
||||||
|
|
||||||
let request = client1.getPurchase(id).get.request.get
|
let request = client1.getPurchase(id).get.request.get
|
||||||
check request.ask.duration == 100.u256
|
check request.ask.duration == 100.u256
|
||||||
check request.ask.reward == 2.u256
|
check request.ask.pricePerBytePerSecond == 1.u256
|
||||||
check request.ask.proofProbability == 3.u256
|
check request.ask.proofProbability == 3.u256
|
||||||
check request.expiry == 30
|
check request.expiry == 30
|
||||||
check request.ask.collateral == 200.u256
|
check request.ask.collateralPerByte == 1.u256
|
||||||
check request.ask.slots == 3'u64
|
check request.ask.slots == 3'u64
|
||||||
check request.ask.maxSlotLoss == 1'u64
|
check request.ask.maxSlotLoss == 1'u64
|
||||||
|
|
||||||
# TODO: We currently do not support encoding single chunks
|
# TODO: We currently do not support encoding single chunks
|
||||||
# test "node retrieves purchase status with 1 chunk", twoNodesConfig:
|
# test "node retrieves purchase status with 1 chunk", twoNodesConfig:
|
||||||
# let cid = client1.upload("some file contents").get
|
# let cid = client1.upload("some file contents").get
|
||||||
# let id = client1.requestStorage(cid, duration=1.u256, reward=2.u256, proofProbability=3.u256, expiry=30, collateral=200.u256, nodes=2, tolerance=1).get
|
# let id = client1.requestStorage(
|
||||||
|
# cid, duration=1.u256, pricePerBytePerSecond=1.u256,
|
||||||
|
# proofProbability=3.u256, expiry=30, collateralPerByte=1.u256,
|
||||||
|
# nodes=2, tolerance=1).get
|
||||||
# let request = client1.getPurchase(id).get.request.get
|
# let request = client1.getPurchase(id).get.request.get
|
||||||
# check request.ask.duration == 1.u256
|
# check request.ask.duration == 1.u256
|
||||||
# check request.ask.reward == 2.u256
|
# check request.ask.pricePerBytePerSecond == 1.u256
|
||||||
# check request.ask.proofProbability == 3.u256
|
# check request.ask.proofProbability == 3.u256
|
||||||
# check request.expiry == 30
|
# check request.expiry == 30
|
||||||
# check request.ask.collateral == 200.u256
|
# check request.ask.collateralPerByte == 1.u256
|
||||||
# check request.ask.slots == 3'u64
|
# check request.ask.slots == 3'u64
|
||||||
# check request.ask.maxSlotLoss == 1'u64
|
# check request.ask.maxSlotLoss == 1'u64
|
||||||
|
|
||||||
@ -74,10 +77,10 @@ twonodessuite "Purchasing":
|
|||||||
let id = client1.requestStorage(
|
let id = client1.requestStorage(
|
||||||
cid,
|
cid,
|
||||||
duration = 10 * 60.u256,
|
duration = 10 * 60.u256,
|
||||||
reward = 2.u256,
|
pricePerBytePerSecond = 1.u256,
|
||||||
proofProbability = 3.u256,
|
proofProbability = 3.u256,
|
||||||
expiry = 5 * 60,
|
expiry = 5 * 60,
|
||||||
collateral = 200.u256,
|
collateralPerByte = 1.u256,
|
||||||
nodes = 3.uint,
|
nodes = 3.uint,
|
||||||
tolerance = 1.uint,
|
tolerance = 1.uint,
|
||||||
).get
|
).get
|
||||||
@ -89,10 +92,10 @@ twonodessuite "Purchasing":
|
|||||||
check eventually(client1.purchaseStateIs(id, "submitted"), timeout = 3 * 60 * 1000)
|
check eventually(client1.purchaseStateIs(id, "submitted"), timeout = 3 * 60 * 1000)
|
||||||
let request = client1.getPurchase(id).get.request.get
|
let request = client1.getPurchase(id).get.request.get
|
||||||
check request.ask.duration == (10 * 60).u256
|
check request.ask.duration == (10 * 60).u256
|
||||||
check request.ask.reward == 2.u256
|
check request.ask.pricePerBytePerSecond == 1.u256
|
||||||
check request.ask.proofProbability == 3.u256
|
check request.ask.proofProbability == 3.u256
|
||||||
check request.expiry == (5 * 60).u256
|
check request.expiry == (5 * 60).u256
|
||||||
check request.ask.collateral == 200.u256
|
check request.ask.collateralPerByte == 1.u256
|
||||||
check request.ask.slots == 3'u64
|
check request.ask.slots == 3'u64
|
||||||
check request.ask.maxSlotLoss == 1'u64
|
check request.ask.maxSlotLoss == 1'u64
|
||||||
|
|
||||||
@ -103,9 +106,9 @@ twonodessuite "Purchasing":
|
|||||||
let responseMissing = client1.requestStorageRaw(
|
let responseMissing = client1.requestStorageRaw(
|
||||||
cid,
|
cid,
|
||||||
duration = 1.u256,
|
duration = 1.u256,
|
||||||
reward = 2.u256,
|
pricePerBytePerSecond = 1.u256,
|
||||||
proofProbability = 3.u256,
|
proofProbability = 3.u256,
|
||||||
collateral = 200.u256,
|
collateralPerByte = 1.u256,
|
||||||
)
|
)
|
||||||
check responseMissing.status == "400 Bad Request"
|
check responseMissing.status == "400 Bad Request"
|
||||||
check responseMissing.body == "Expiry required"
|
check responseMissing.body == "Expiry required"
|
||||||
@ -113,9 +116,9 @@ twonodessuite "Purchasing":
|
|||||||
let responseBefore = client1.requestStorageRaw(
|
let responseBefore = client1.requestStorageRaw(
|
||||||
cid,
|
cid,
|
||||||
duration = 10.u256,
|
duration = 10.u256,
|
||||||
reward = 2.u256,
|
pricePerBytePerSecond = 1.u256,
|
||||||
proofProbability = 3.u256,
|
proofProbability = 3.u256,
|
||||||
collateral = 200.u256,
|
collateralPerByte = 1.u256,
|
||||||
expiry = 10,
|
expiry = 10,
|
||||||
)
|
)
|
||||||
check responseBefore.status == "400 Bad Request"
|
check responseBefore.status == "400 Bad Request"
|
||||||
|
|||||||
@ -21,8 +21,14 @@ twonodessuite "REST API":
|
|||||||
|
|
||||||
test "node shows used and available space", twoNodesConfig:
|
test "node shows used and available space", twoNodesConfig:
|
||||||
discard client1.upload("some file contents").get
|
discard client1.upload("some file contents").get
|
||||||
|
let totalSize = 12.u256
|
||||||
|
let minPricePerBytePerSecond = 1.u256
|
||||||
|
let totalCollateral = totalSize * minPricePerBytePerSecond
|
||||||
discard client1.postAvailability(
|
discard client1.postAvailability(
|
||||||
totalSize = 12.u256, duration = 2.u256, minPrice = 3.u256, maxCollateral = 4.u256
|
totalSize = totalSize,
|
||||||
|
duration = 2.u256,
|
||||||
|
minPricePerBytePerSecond = minPricePerBytePerSecond,
|
||||||
|
totalCollateral = totalCollateral,
|
||||||
).get
|
).get
|
||||||
let space = client1.space().tryGet()
|
let space = client1.space().tryGet()
|
||||||
check:
|
check:
|
||||||
@ -47,9 +53,9 @@ twonodessuite "REST API":
|
|||||||
let response = client1.requestStorageRaw(
|
let response = client1.requestStorageRaw(
|
||||||
cid,
|
cid,
|
||||||
duration = 10.u256,
|
duration = 10.u256,
|
||||||
reward = 2.u256,
|
pricePerBytePerSecond = 1.u256,
|
||||||
proofProbability = 3.u256,
|
proofProbability = 3.u256,
|
||||||
collateral = 200.u256,
|
collateralPerByte = 1.u256,
|
||||||
expiry = 9,
|
expiry = 9,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -65,9 +71,9 @@ twonodessuite "REST API":
|
|||||||
let response = client1.requestStorageRaw(
|
let response = client1.requestStorageRaw(
|
||||||
cid,
|
cid,
|
||||||
duration = 10.u256,
|
duration = 10.u256,
|
||||||
reward = 2.u256,
|
pricePerBytePerSecond = 1.u256,
|
||||||
proofProbability = 3.u256,
|
proofProbability = 3.u256,
|
||||||
collateral = 200.u256,
|
collateralPerByte = 1.u256,
|
||||||
expiry = 9,
|
expiry = 9,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -78,16 +84,16 @@ twonodessuite "REST API":
|
|||||||
let data = await RandomChunker.example(blocks = 2)
|
let data = await RandomChunker.example(blocks = 2)
|
||||||
let cid = client1.upload(data).get
|
let cid = client1.upload(data).get
|
||||||
let duration = 100.u256
|
let duration = 100.u256
|
||||||
let reward = 2.u256
|
let pricePerBytePerSecond = 1.u256
|
||||||
let proofProbability = 3.u256
|
let proofProbability = 3.u256
|
||||||
let expiry = 30.uint
|
let expiry = 30.uint
|
||||||
let collateral = 200.u256
|
let collateralPerByte = 1.u256
|
||||||
let nodes = 3
|
let nodes = 3
|
||||||
let tolerance = 0
|
let tolerance = 0
|
||||||
|
|
||||||
var responseBefore = client1.requestStorageRaw(
|
var responseBefore = client1.requestStorageRaw(
|
||||||
cid, duration, reward, proofProbability, collateral, expiry, nodes.uint,
|
cid, duration, pricePerBytePerSecond, proofProbability, collateralPerByte, expiry,
|
||||||
tolerance.uint,
|
nodes.uint, tolerance.uint,
|
||||||
)
|
)
|
||||||
|
|
||||||
check responseBefore.status == "400 Bad Request"
|
check responseBefore.status == "400 Bad Request"
|
||||||
@ -97,18 +103,18 @@ twonodessuite "REST API":
|
|||||||
let data = await RandomChunker.example(blocks = 2)
|
let data = await RandomChunker.example(blocks = 2)
|
||||||
let cid = client1.upload(data).get
|
let cid = client1.upload(data).get
|
||||||
let duration = 100.u256
|
let duration = 100.u256
|
||||||
let reward = 2.u256
|
let pricePerBytePerSecond = 1.u256
|
||||||
let proofProbability = 3.u256
|
let proofProbability = 3.u256
|
||||||
let expiry = 30.uint
|
let expiry = 30.uint
|
||||||
let collateral = 200.u256
|
let collateralPerByte = 1.u256
|
||||||
let ecParams = @[(1, 1), (2, 1), (3, 2), (3, 3)]
|
let ecParams = @[(1, 1), (2, 1), (3, 2), (3, 3)]
|
||||||
|
|
||||||
for ecParam in ecParams:
|
for ecParam in ecParams:
|
||||||
let (nodes, tolerance) = ecParam
|
let (nodes, tolerance) = ecParam
|
||||||
|
|
||||||
var responseBefore = client1.requestStorageRaw(
|
var responseBefore = client1.requestStorageRaw(
|
||||||
cid, duration, reward, proofProbability, collateral, expiry, nodes.uint,
|
cid, duration, pricePerBytePerSecond, proofProbability, collateralPerByte,
|
||||||
tolerance.uint,
|
expiry, nodes.uint, tolerance.uint,
|
||||||
)
|
)
|
||||||
|
|
||||||
check responseBefore.status == "400 Bad Request"
|
check responseBefore.status == "400 Bad Request"
|
||||||
@ -120,18 +126,18 @@ twonodessuite "REST API":
|
|||||||
let data = await RandomChunker.example(blocks = 2)
|
let data = await RandomChunker.example(blocks = 2)
|
||||||
let cid = client1.upload(data).get
|
let cid = client1.upload(data).get
|
||||||
let duration = 100.u256
|
let duration = 100.u256
|
||||||
let reward = 2.u256
|
let pricePerBytePerSecond = 1.u256
|
||||||
let proofProbability = 3.u256
|
let proofProbability = 3.u256
|
||||||
let expiry = 30.uint
|
let expiry = 30.uint
|
||||||
let collateral = 200.u256
|
let collateralPerByte = 1.u256
|
||||||
let ecParams = @[(0, 1), (1, 2), (2, 3)]
|
let ecParams = @[(0, 1), (1, 2), (2, 3)]
|
||||||
|
|
||||||
for ecParam in ecParams:
|
for ecParam in ecParams:
|
||||||
let (nodes, tolerance) = ecParam
|
let (nodes, tolerance) = ecParam
|
||||||
|
|
||||||
var responseBefore = client1.requestStorageRaw(
|
var responseBefore = client1.requestStorageRaw(
|
||||||
cid, duration, reward, proofProbability, collateral, expiry, nodes.uint,
|
cid, duration, pricePerBytePerSecond, proofProbability, collateralPerByte,
|
||||||
tolerance.uint,
|
expiry, nodes.uint, tolerance.uint,
|
||||||
)
|
)
|
||||||
|
|
||||||
check responseBefore.status == "400 Bad Request"
|
check responseBefore.status == "400 Bad Request"
|
||||||
@ -142,18 +148,18 @@ twonodessuite "REST API":
|
|||||||
let data = await RandomChunker.example(blocks = 2)
|
let data = await RandomChunker.example(blocks = 2)
|
||||||
let cid = client1.upload(data).get
|
let cid = client1.upload(data).get
|
||||||
let duration = 100.u256
|
let duration = 100.u256
|
||||||
let reward = 2.u256
|
let pricePerBytePerSecond = 1.u256
|
||||||
let proofProbability = 3.u256
|
let proofProbability = 3.u256
|
||||||
let expiry = 30.uint
|
let expiry = 30.uint
|
||||||
let collateral = 200.u256
|
let collateralPerByte = 1.u256
|
||||||
let ecParams = @[(3, 1), (5, 2)]
|
let ecParams = @[(3, 1), (5, 2)]
|
||||||
|
|
||||||
for ecParam in ecParams:
|
for ecParam in ecParams:
|
||||||
let (nodes, tolerance) = ecParam
|
let (nodes, tolerance) = ecParam
|
||||||
|
|
||||||
var responseBefore = client1.requestStorageRaw(
|
var responseBefore = client1.requestStorageRaw(
|
||||||
cid, duration, reward, proofProbability, collateral, expiry, nodes.uint,
|
cid, duration, pricePerBytePerSecond, proofProbability, collateralPerByte,
|
||||||
tolerance.uint,
|
expiry, nodes.uint, tolerance.uint,
|
||||||
)
|
)
|
||||||
|
|
||||||
check responseBefore.status == "200 OK"
|
check responseBefore.status == "200 OK"
|
||||||
|
|||||||
@ -20,6 +20,8 @@ multinodesuite "Sales":
|
|||||||
providers: CodexConfigs.init(nodes = 1).some,
|
providers: CodexConfigs.init(nodes = 1).some,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
let minPricePerBytePerSecond = 1.u256
|
||||||
|
|
||||||
var host: CodexClient
|
var host: CodexClient
|
||||||
var client: CodexClient
|
var client: CodexClient
|
||||||
|
|
||||||
@ -29,16 +31,25 @@ multinodesuite "Sales":
|
|||||||
|
|
||||||
test "node handles new storage availability", salesConfig:
|
test "node handles new storage availability", salesConfig:
|
||||||
let availability1 = host.postAvailability(
|
let availability1 = host.postAvailability(
|
||||||
totalSize = 1.u256, duration = 2.u256, minPrice = 3.u256, maxCollateral = 4.u256
|
totalSize = 1.u256,
|
||||||
|
duration = 2.u256,
|
||||||
|
minPricePerBytePerSecond = 3.u256,
|
||||||
|
totalCollateral = 4.u256,
|
||||||
).get
|
).get
|
||||||
let availability2 = host.postAvailability(
|
let availability2 = host.postAvailability(
|
||||||
totalSize = 4.u256, duration = 5.u256, minPrice = 6.u256, maxCollateral = 7.u256
|
totalSize = 4.u256,
|
||||||
|
duration = 5.u256,
|
||||||
|
minPricePerBytePerSecond = 6.u256,
|
||||||
|
totalCollateral = 7.u256,
|
||||||
).get
|
).get
|
||||||
check availability1 != availability2
|
check availability1 != availability2
|
||||||
|
|
||||||
test "node lists storage that is for sale", salesConfig:
|
test "node lists storage that is for sale", salesConfig:
|
||||||
let availability = host.postAvailability(
|
let availability = host.postAvailability(
|
||||||
totalSize = 1.u256, duration = 2.u256, minPrice = 3.u256, maxCollateral = 4.u256
|
totalSize = 1.u256,
|
||||||
|
duration = 2.u256,
|
||||||
|
minPricePerBytePerSecond = 3.u256,
|
||||||
|
totalCollateral = 4.u256,
|
||||||
).get
|
).get
|
||||||
check availability in host.getAvailabilities().get
|
check availability in host.getAvailabilities().get
|
||||||
|
|
||||||
@ -46,8 +57,8 @@ multinodesuite "Sales":
|
|||||||
let nonExistingResponse = host.patchAvailabilityRaw(
|
let nonExistingResponse = host.patchAvailabilityRaw(
|
||||||
AvailabilityId.example,
|
AvailabilityId.example,
|
||||||
duration = 100.u256.some,
|
duration = 100.u256.some,
|
||||||
minPrice = 200.u256.some,
|
minPricePerBytePerSecond = 2.u256.some,
|
||||||
maxCollateral = 200.u256.some,
|
totalCollateral = 200.u256.some,
|
||||||
)
|
)
|
||||||
check nonExistingResponse.status == "404 Not Found"
|
check nonExistingResponse.status == "404 Not Found"
|
||||||
|
|
||||||
@ -55,21 +66,21 @@ multinodesuite "Sales":
|
|||||||
let availability = host.postAvailability(
|
let availability = host.postAvailability(
|
||||||
totalSize = 140000.u256,
|
totalSize = 140000.u256,
|
||||||
duration = 200.u256,
|
duration = 200.u256,
|
||||||
minPrice = 300.u256,
|
minPricePerBytePerSecond = 3.u256,
|
||||||
maxCollateral = 300.u256,
|
totalCollateral = 300.u256,
|
||||||
).get
|
).get
|
||||||
|
|
||||||
host.patchAvailability(
|
host.patchAvailability(
|
||||||
availability.id,
|
availability.id,
|
||||||
duration = 100.u256.some,
|
duration = 100.u256.some,
|
||||||
minPrice = 200.u256.some,
|
minPricePerBytePerSecond = 2.u256.some,
|
||||||
maxCollateral = 200.u256.some,
|
totalCollateral = 200.u256.some,
|
||||||
)
|
)
|
||||||
|
|
||||||
let updatedAvailability = (host.getAvailabilities().get).findItem(availability).get
|
let updatedAvailability = (host.getAvailabilities().get).findItem(availability).get
|
||||||
check updatedAvailability.duration == 100
|
check updatedAvailability.duration == 100
|
||||||
check updatedAvailability.minPrice == 200
|
check updatedAvailability.minPricePerBytePerSecond == 2
|
||||||
check updatedAvailability.maxCollateral == 200
|
check updatedAvailability.totalCollateral == 200
|
||||||
check updatedAvailability.totalSize == 140000
|
check updatedAvailability.totalSize == 140000
|
||||||
check updatedAvailability.freeSize == 140000
|
check updatedAvailability.freeSize == 140000
|
||||||
|
|
||||||
@ -77,8 +88,8 @@ multinodesuite "Sales":
|
|||||||
let availability = host.postAvailability(
|
let availability = host.postAvailability(
|
||||||
totalSize = 140000.u256,
|
totalSize = 140000.u256,
|
||||||
duration = 200.u256,
|
duration = 200.u256,
|
||||||
minPrice = 300.u256,
|
minPricePerBytePerSecond = 3.u256,
|
||||||
maxCollateral = 300.u256,
|
totalCollateral = 300.u256,
|
||||||
).get
|
).get
|
||||||
let freeSizeResponse =
|
let freeSizeResponse =
|
||||||
host.patchAvailabilityRaw(availability.id, freeSize = 110000.u256.some)
|
host.patchAvailabilityRaw(availability.id, freeSize = 110000.u256.some)
|
||||||
@ -89,8 +100,8 @@ multinodesuite "Sales":
|
|||||||
let availability = host.postAvailability(
|
let availability = host.postAvailability(
|
||||||
totalSize = 140000.u256,
|
totalSize = 140000.u256,
|
||||||
duration = 200.u256,
|
duration = 200.u256,
|
||||||
minPrice = 300.u256,
|
minPricePerBytePerSecond = 3.u256,
|
||||||
maxCollateral = 300.u256,
|
totalCollateral = 300.u256,
|
||||||
).get
|
).get
|
||||||
host.patchAvailability(availability.id, totalSize = 100000.u256.some)
|
host.patchAvailability(availability.id, totalSize = 100000.u256.some)
|
||||||
let updatedAvailability = (host.getAvailabilities().get).findItem(availability).get
|
let updatedAvailability = (host.getAvailabilities().get).findItem(availability).get
|
||||||
@ -101,11 +112,14 @@ multinodesuite "Sales":
|
|||||||
salesConfig:
|
salesConfig:
|
||||||
let originalSize = 0xFFFFFF.u256
|
let originalSize = 0xFFFFFF.u256
|
||||||
let data = await RandomChunker.example(blocks = 8)
|
let data = await RandomChunker.example(blocks = 8)
|
||||||
|
let minPricePerBytePerSecond = 3.u256
|
||||||
|
let collateralPerByte = 1.u256
|
||||||
|
let totalCollateral = originalSize * collateralPerByte
|
||||||
let availability = host.postAvailability(
|
let availability = host.postAvailability(
|
||||||
totalSize = originalSize,
|
totalSize = originalSize,
|
||||||
duration = 20 * 60.u256,
|
duration = 20 * 60.u256,
|
||||||
minPrice = 300.u256,
|
minPricePerBytePerSecond = minPricePerBytePerSecond,
|
||||||
maxCollateral = 300.u256,
|
totalCollateral = totalCollateral,
|
||||||
).get
|
).get
|
||||||
|
|
||||||
# Lets create storage request that will utilize some of the availability's space
|
# Lets create storage request that will utilize some of the availability's space
|
||||||
@ -113,10 +127,10 @@ multinodesuite "Sales":
|
|||||||
let id = client.requestStorage(
|
let id = client.requestStorage(
|
||||||
cid,
|
cid,
|
||||||
duration = 20 * 60.u256,
|
duration = 20 * 60.u256,
|
||||||
reward = 400.u256,
|
pricePerBytePerSecond = minPricePerBytePerSecond,
|
||||||
proofProbability = 3.u256,
|
proofProbability = 3.u256,
|
||||||
expiry = 10 * 60,
|
expiry = 10 * 60,
|
||||||
collateral = 200.u256,
|
collateralPerByte = collateralPerByte,
|
||||||
nodes = 3,
|
nodes = 3,
|
||||||
tolerance = 1,
|
tolerance = 1,
|
||||||
).get
|
).get
|
||||||
|
|||||||
@ -34,9 +34,13 @@ template eventuallyS(
|
|||||||
await eventuallyS()
|
await eventuallyS()
|
||||||
|
|
||||||
marketplacesuite "Validation":
|
marketplacesuite "Validation":
|
||||||
let nodes = 3
|
const blocks = 8
|
||||||
let tolerance = 1
|
const ecNodes = 3
|
||||||
let proofProbability = 1
|
const ecTolerance = 1
|
||||||
|
const proofProbability = 1
|
||||||
|
|
||||||
|
const collateralPerByte = 1.u256
|
||||||
|
const minPricePerBytePerSecond = 1.u256
|
||||||
|
|
||||||
proc waitForRequestToFail(
|
proc waitForRequestToFail(
|
||||||
marketplace: Marketplace, requestId: RequestId, timeout = 10, step = 5
|
marketplace: Marketplace, requestId: RequestId, timeout = 10, step = 5
|
||||||
@ -92,19 +96,20 @@ marketplacesuite "Validation":
|
|||||||
var currentTime = await ethProvider.currentTime()
|
var currentTime = await ethProvider.currentTime()
|
||||||
let requestEndTime = currentTime.truncate(uint64) + duration
|
let requestEndTime = currentTime.truncate(uint64) + duration
|
||||||
|
|
||||||
let data = await RandomChunker.example(blocks = 8)
|
let data = await RandomChunker.example(blocks = blocks)
|
||||||
|
let datasetSize =
|
||||||
# TODO: better value for data.len below. This TODO is also present in
|
datasetSize(blocks = blocks, nodes = ecNodes, tolerance = ecTolerance)
|
||||||
# testproofs.nim - we may want to address it or remove the comment.
|
createAvailabilities(
|
||||||
createAvailabilities(data.len * 2, duration)
|
datasetSize, duration, collateralPerByte, minPricePerBytePerSecond
|
||||||
|
)
|
||||||
|
|
||||||
let cid = client0.upload(data).get
|
let cid = client0.upload(data).get
|
||||||
let purchaseId = await client0.requestStorage(
|
let purchaseId = await client0.requestStorage(
|
||||||
cid,
|
cid,
|
||||||
expiry = expiry,
|
expiry = expiry,
|
||||||
duration = duration,
|
duration = duration,
|
||||||
nodes = nodes,
|
nodes = ecNodes,
|
||||||
tolerance = tolerance,
|
tolerance = ecTolerance,
|
||||||
proofProbability = proofProbability,
|
proofProbability = proofProbability,
|
||||||
)
|
)
|
||||||
let requestId = client0.requestId(purchaseId).get
|
let requestId = client0.requestId(purchaseId).get
|
||||||
@ -158,19 +163,20 @@ marketplacesuite "Validation":
|
|||||||
var currentTime = await ethProvider.currentTime()
|
var currentTime = await ethProvider.currentTime()
|
||||||
let requestEndTime = currentTime.truncate(uint64) + duration
|
let requestEndTime = currentTime.truncate(uint64) + duration
|
||||||
|
|
||||||
let data = await RandomChunker.example(blocks = 8)
|
let data = await RandomChunker.example(blocks = blocks)
|
||||||
|
let datasetSize =
|
||||||
# TODO: better value for data.len below. This TODO is also present in
|
datasetSize(blocks = blocks, nodes = ecNodes, tolerance = ecTolerance)
|
||||||
# testproofs.nim - we may want to address it or remove the comment.
|
createAvailabilities(
|
||||||
createAvailabilities(data.len * 2, duration)
|
datasetSize, duration, collateralPerByte, minPricePerBytePerSecond
|
||||||
|
)
|
||||||
|
|
||||||
let cid = client0.upload(data).get
|
let cid = client0.upload(data).get
|
||||||
let purchaseId = await client0.requestStorage(
|
let purchaseId = await client0.requestStorage(
|
||||||
cid,
|
cid,
|
||||||
expiry = expiry,
|
expiry = expiry,
|
||||||
duration = duration,
|
duration = duration,
|
||||||
nodes = nodes,
|
nodes = ecNodes,
|
||||||
tolerance = tolerance,
|
tolerance = ecTolerance,
|
||||||
proofProbability = proofProbability,
|
proofProbability = proofProbability,
|
||||||
)
|
)
|
||||||
let requestId = client0.requestId(purchaseId).get
|
let requestId = client0.requestId(purchaseId).get
|
||||||
|
|||||||
@ -46,11 +46,14 @@ suite "Taiko L2 Integration Tests":
|
|||||||
node2.removeDataDir()
|
node2.removeDataDir()
|
||||||
|
|
||||||
test "node 1 buys storage from node 2":
|
test "node 1 buys storage from node 2":
|
||||||
|
let size = 0xFFFFF.u256
|
||||||
|
let minPricePerBytePerSecond = 1.u256
|
||||||
|
let totalCollateral = size * minPricePerBytePerSecond
|
||||||
discard node2.client.postAvailability(
|
discard node2.client.postAvailability(
|
||||||
size = 0xFFFFF.u256,
|
size = size,
|
||||||
duration = 200.u256,
|
duration = 200.u256,
|
||||||
minPrice = 300.u256,
|
minPricePerBytePerSecond = minPricePerBytePerSecond,
|
||||||
maxCollateral = 300.u256,
|
totalCollateral = totalCollateral,
|
||||||
)
|
)
|
||||||
let cid = !node1.client.upload("some file contents")
|
let cid = !node1.client.upload("some file contents")
|
||||||
|
|
||||||
@ -60,9 +63,9 @@ suite "Taiko L2 Integration Tests":
|
|||||||
!node1.client.requestStorage(
|
!node1.client.requestStorage(
|
||||||
cid,
|
cid,
|
||||||
duration = 30.u256,
|
duration = 30.u256,
|
||||||
reward = 400.u256,
|
pricePerBytePerSecond = 1.u256,
|
||||||
proofProbability = 3.u256,
|
proofProbability = 3.u256,
|
||||||
collateral = 200.u256,
|
collateralPerByte = 1.u256,
|
||||||
expiry = expiry.u256,
|
expiry = expiry.u256,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
2
vendor/codex-contracts-eth
vendored
2
vendor/codex-contracts-eth
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 02e3b8d22b15975fd450ccdad30e32c0be7e593f
|
Subproject commit e74d3397a133eaf1eb95d9ce59f56747a7c8c30b
|
||||||
Loading…
x
Reference in New Issue
Block a user