contracts: introduce types for time and tokens

fixes bugs with slotqueue and repostore, where
expiry duration was misinterpreted as expiry
timestamp
This commit is contained in:
Mark Spanbroek 2025-03-31 10:42:32 +02:00
parent 46ca467449
commit ea3b9916b2
No known key found for this signature in database
GPG Key ID: FBE3E9548D427C00
62 changed files with 1175 additions and 882 deletions

View File

@ -1,7 +1,6 @@
import pkg/chronos
import pkg/stew/endians2
import pkg/upraises
import pkg/stint
type
Clock* = ref object of RootObj
@ -42,6 +41,3 @@ proc toSecondsSince1970*(bytes: seq[byte]): SecondsSince1970 =
proc toSecondsSince1970*(num: uint64): SecondsSince1970 =
cast[int64](num)
proc toSecondsSince1970*(bigint: StUint[40]): SecondsSince1970 =
bigint.truncate(int64)

View File

@ -1,6 +1,7 @@
import pkg/contractabi
import pkg/ethers/contracts/fields
import pkg/questionable/results
import ./requests
export contractabi
@ -11,7 +12,7 @@ type
collateral*: CollateralConfig
proofs*: ProofConfig
reservations*: SlotReservationsConfig
requestDurationLimit*: StUint[40]
requestDurationLimit*: StorageDuration
CollateralConfig* = object
repairRewardPercentage*: uint8
@ -22,8 +23,8 @@ type
# percentage of the slashed amount going to the validators
ProofConfig* = object
period*: StUint[40] # proofs requirements are calculated per period (in seconds)
timeout*: StUint[40] # mark proofs as missing before the timeout (in seconds)
period*: StorageDuration # proofs requirements are calculated per period (in seconds)
timeout*: StorageDuration # mark proofs as missing before the timeout (in seconds)
downtime*: uint8 # ignore this much recent blocks for proof requirements
downtimeProduct*: uint8
zkeyHash*: string # hash of the zkey file which is linked to the verifier

View File

@ -89,7 +89,7 @@ template withAllowanceLock*(market: OnChainMarket, body: untyped) =
raise newException(Defect, error.msg, error)
proc approveFunds(
market: OnChainMarket, amount: UInt256
market: OnChainMarket, amount: Tokens
) {.async: (raises: [CancelledError, MarketError]).} =
debug "Approving tokens", amount
convertEthersError("Failed to approve funds"):
@ -99,7 +99,7 @@ proc approveFunds(
let spender = market.contract.address
market.withAllowanceLock:
let allowance = await token.allowance(owner, spender)
discard await token.approve(spender, allowance + amount).confirm(1)
discard await token.approve(spender, allowance + amount.u256).confirm(1)
method getSigner*(
market: OnChainMarket
@ -112,16 +112,16 @@ method zkeyHash*(market: OnChainMarket): string =
method periodicity*(market: OnChainMarket): Periodicity =
let period = market.configuration.proofs.period
return Periodicity(seconds: period.u64)
return Periodicity(seconds: period)
method proofTimeout*(market: OnChainMarket): uint64 =
return market.configuration.proofs.timeout.u64
method proofTimeout*(market: OnChainMarket): StorageDuration =
return market.configuration.proofs.timeout
method repairRewardPercentage*(market: OnChainMarket): uint8 =
return market.configuration.collateral.repairRewardPercentage
method requestDurationLimit*(market: OnChainMarket): uint64 =
return market.configuration.requestDurationLimit.u64
method requestDurationLimit*(market: OnChainMarket): StorageDuration =
return market.configuration.requestDurationLimit
method proofDowntime*(market: OnChainMarket): uint8 =
return market.configuration.proofs.downtime
@ -147,7 +147,7 @@ method requestStorage(
) {.async: (raises: [CancelledError, MarketError]).} =
convertEthersError("Failed to request storage"):
debug "Requesting storage"
await market.approveFunds(request.totalPrice().stuint(256))
await market.approveFunds(request.totalPrice())
discard await market.contract.requestStorage(request).confirm(1)
method getRequest*(
@ -188,13 +188,13 @@ method slotState*(
method getRequestEnd*(
market: OnChainMarket, id: RequestId
): Future[SecondsSince1970] {.async.} =
): Future[StorageTimestamp] {.async.} =
convertEthersError("Failed to get request end"):
return await market.contract.requestEnd(id)
method requestExpiresAt*(
market: OnChainMarket, id: RequestId
): Future[SecondsSince1970] {.async.} =
): Future[StorageTimestamp] {.async.} =
convertEthersError("Failed to get request expiry"):
return await market.contract.requestExpiry(id)
@ -211,7 +211,7 @@ method getHost(
method currentCollateral*(
market: OnChainMarket, slotId: SlotId
): Future[UInt128] {.async: (raises: [MarketError, CancelledError]).} =
): Future[Tokens] {.async: (raises: [MarketError, CancelledError]).} =
convertEthersError("Failed to get slot's current collateral"):
return await market.contract.currentCollateral(slotId)
@ -227,7 +227,7 @@ method fillSlot(
requestId: RequestId,
slotIndex: uint64,
proof: Groth16Proof,
collateral: UInt128,
collateral: Tokens,
) {.async: (raises: [CancelledError, MarketError]).} =
convertEthersError("Failed to fill slot"):
logScope:
@ -235,7 +235,7 @@ method fillSlot(
slotIndex
try:
await market.approveFunds(collateral.stuint(256))
await market.approveFunds(collateral)
# Add 10% to gas estimate to deal with different evm code flow when we
# happen to be the last one to fill a slot in this request
@ -306,7 +306,7 @@ method submitProof*(
discard await market.contract.submitProof(id, proof).confirm(1)
method markProofAsMissing*(
market: OnChainMarket, id: SlotId, period: Period
market: OnChainMarket, id: SlotId, period: ProofPeriod
) {.async: (raises: [CancelledError, MarketError]).} =
convertEthersError("Failed to mark proof as missing"):
# Add 10% to gas estimate to deal with different evm code flow when we
@ -314,16 +314,16 @@ method markProofAsMissing*(
let gas = await market.contract.estimateGas.markProofAsMissing(id, period)
let overrides = TransactionOverrides(gasLimit: some (gas * 110) div 100)
discard await market.contract.markProofAsMissing(id, period.stuint(40), overrides).confirm(1)
discard await market.contract.markProofAsMissing(id, period, overrides).confirm(1)
method canProofBeMarkedAsMissing*(
market: OnChainMarket, id: SlotId, period: Period
market: OnChainMarket, id: SlotId, period: ProofPeriod
): Future[bool] {.async.} =
let provider = market.contract.provider
let contractWithoutSigner = market.contract.connect(provider)
let overrides = CallOverrides(blockTag: some BlockTag.pending)
try:
discard await contractWithoutSigner.markProofAsMissing(id, period.stuint(40), overrides)
discard await contractWithoutSigner.markProofAsMissing(id, period, overrides)
return true
except EthersError as e:
trace "Proof cannot be marked as missing", msg = e.msg
@ -361,7 +361,7 @@ method subscribeRequests*(
error "There was an error in Request subscription", msg = eventErr.msg
return
callback(event.requestId, event.ask, event.expiry.truncate(uint64))
callback(event.requestId, event.ask, event.expiry)
convertEthersError("Failed to subscribe to StorageRequested events"):
let subscription = await market.contract.subscribe(StorageRequested, onEvent)

View File

@ -58,7 +58,7 @@ proc configuration*(marketplace: Marketplace): MarketplaceConfig {.contract, vie
proc token*(marketplace: Marketplace): Address {.contract, view.}
proc currentCollateral*(
marketplace: Marketplace, id: SlotId
): UInt128 {.contract, view.}
): Tokens {.contract, view.}
proc requestStorage*(
marketplace: Marketplace, request: StorageRequest
@ -131,11 +131,11 @@ proc requestState*(
proc slotState*(marketplace: Marketplace, slotId: SlotId): SlotState {.contract, view.}
proc requestEnd*(
marketplace: Marketplace, requestId: RequestId
): SecondsSince1970 {.contract, view.}
): StorageTimestamp {.contract, view.}
proc requestExpiry*(
marketplace: Marketplace, requestId: RequestId
): SecondsSince1970 {.contract, view.}
): StorageTimestamp {.contract, view.}
proc missingProofs*(marketplace: Marketplace, id: SlotId): UInt256 {.contract, view.}
proc isProofRequired*(marketplace: Marketplace, id: SlotId): bool {.contract, view.}
@ -155,7 +155,7 @@ proc submitProof*(
.}
proc markProofAsMissing*(
marketplace: Marketplace, id: SlotId, period: StUint[40]
marketplace: Marketplace, id: SlotId, period: ProofPeriod
): Confirmable {.
contract,
errors: [

View File

@ -0,0 +1,18 @@
import ../clock
import ./requests
type
Periodicity* = object
seconds*: StorageDuration
func periodOf*(periodicity: Periodicity, timestamp: StorageTimestamp): ProofPeriod =
ProofPeriod.init(timestamp.u40 div periodicity.seconds.u40)
func periodOf*(periodicity: Periodicity, timestamp: SecondsSince1970): ProofPeriod =
periodicity.periodOf(StorageTimestamp.init(timestamp))
func periodStart*(periodicity: Periodicity, period: ProofPeriod): StorageTimestamp =
StorageTimestamp.init(period.u40 * periodicity.seconds.u40)
func periodEnd*(periodicity: Periodicity, period: ProofPeriod): StorageTimestamp =
periodicity.periodStart(period + 1'u8)

View File

@ -10,8 +10,12 @@ import pkg/libp2p/[cid, multicodec]
import ../logutils
import ../utils/json
from ../errors import mapFailure
import ./timestamps
import ./tokens
export contractabi
export timestamps
export tokens
type
StorageRequest* = object
@ -24,7 +28,7 @@ type
StorageAsk* = object
proofProbability* {.serialize.}: UInt256
pricePerBytePerSecond* {.serialize.}: TokensPerSecond
collateralPerByte* {.serialize.}: UInt128
collateralPerByte* {.serialize.}: Tokens
slots* {.serialize.}: uint64
slotSize* {.serialize.}: uint64
duration* {.serialize.}: StorageDuration
@ -56,9 +60,6 @@ type
Cancelled
Repair
StorageDuration* = StUint[40]
TokensPerSecond* = StUint[96]
proc `==`*(x, y: Nonce): bool {.borrow.}
proc `==`*(x, y: RequestId): bool {.borrow.}
proc `==`*(x, y: SlotId): bool {.borrow.}
@ -69,9 +70,6 @@ proc hash*(x: Address): Hash {.borrow.}
func toArray*(id: RequestId | SlotId | Nonce): array[32, byte] =
array[32, byte](id)
func u64*(uint40: StUint[40]): uint64 =
uint40.truncate(uint64)
proc `$`*(id: RequestId | SlotId | Nonce): string =
id.toArray.toHex
@ -139,12 +137,6 @@ func solidityType*(_: type StorageAsk): string =
func solidityType*(_: type StorageRequest): string =
solidityType(StorageRequest.fieldTypes)
func solidityType*(_: type StUint[40]): string =
"uint40"
func solidityType*(_: type StUint[96]): string =
"uint96"
# Note: it seems to be ok to ignore the vbuffer offset for now
func encode*(encoder: var AbiEncoder, cid: Cid) =
encoder.write(cid.data.buffer)
@ -199,19 +191,19 @@ func id*(slot: Slot): SlotId =
slotId(slot.request, slot.slotIndex)
func pricePerSlotPerSecond*(ask: StorageAsk): TokensPerSecond =
ask.pricePerBytePerSecond * ask.slotSize.to(TokensPerSecond)
ask.pricePerBytePerSecond * ask.slotSize
func pricePerSlot*(ask: StorageAsk): UInt128 =
ask.duration.stuint(128) * ask.pricePerSlotPerSecond.stuint(128)
func pricePerSlot*(ask: StorageAsk): Tokens =
ask.pricePerSlotPerSecond * ask.duration
func totalPrice*(ask: StorageAsk): UInt128 =
ask.slots.stuint(128) * ask.pricePerSlot
func totalPrice*(ask: StorageAsk): Tokens =
ask.pricePerSlot * ask.slots
func totalPrice*(request: StorageRequest): UInt128 =
func totalPrice*(request: StorageRequest): Tokens =
request.ask.totalPrice
func collateralPerSlot*(ask: StorageAsk): UInt128 =
ask.collateralPerByte * ask.slotSize.stuint(128)
func collateralPerSlot*(ask: StorageAsk): Tokens =
ask.collateralPerByte * ask.slotSize
func size*(ask: StorageAsk): uint64 =
ask.slots * ask.slotSize

View File

@ -0,0 +1,168 @@
import pkg/stint
import pkg/contractabi
import ../utils/json
from ../clock import SecondsSince1970
type
StorageTimestamp* = object
value: StUint[40]
StorageDuration* = object
value: StUint[40]
ProofPeriod* = object
value: StUint[40]
func u40*(duration: StorageDuration): StUint[40] =
duration.value
func u40*(duration: StorageTimestamp): StUint[40] =
duration.value
func u40*(period: ProofPeriod): StUint[40] =
period.value
func u64*(duration: StorageDuration): uint64 =
duration.value.truncate(uint64)
func u64*(timestamp: StorageTimestamp): uint64 =
timestamp.value.truncate(uint64)
func u64*(period: ProofPeriod): uint64 =
period.value.truncate(uint64)
func u256*(timestamp: StorageTimestamp): UInt256 =
timestamp.value.stuint(256)
func u256*(duration: StorageDuration): UInt256 =
duration.value.stuint(256)
proc toSecondsSince1970*(timestamp: StorageTimestamp): SecondsSince1970 =
timestamp.value.truncate(int64)
func `'StorageDuration`*(value: static string): StorageDuration =
const parsed = parse(value, StUint[40])
StorageDuration(value: parsed)
func `'StorageTimestamp`*(value: static string): StorageTimestamp =
const parsed =parse(value, StUint[40])
StorageTimestamp(value: parsed)
func init*(_: type StorageDuration, value: StUint[40]): StorageDuration =
StorageDuration(value: value)
func init*(_: type StorageDuration, value: uint32 | uint16 | uint8): StorageDuration =
StorageDuration.init(value.stuint(40))
func init*(_: type StorageTimestamp, value: StUint[40]): StorageTimestamp =
StorageTimestamp(value: value)
func init*(_: type StorageTimestamp, value: uint32 | uint16 | uint8): StorageTimestamp =
StorageTimestamp.init(value.stuint(40))
func init*(_: type StorageTimestamp, value: SecondsSince1970): StorageTimestamp =
# The maximum timestamp is 2^40-1 seconds after 1970, which is the year 36,835
const maximum = StUint[40].high.truncate(SecondsSince1970)
if value > maximum:
# make sure that we don't wrap around to a time in the past
return StorageTimestamp.init(maximum.stuint(40))
StorageTimestamp.init(value.stuint(40))
func init*(_: type ProofPeriod, value: StUint[40]): ProofPeriod =
ProofPeriod(value: value)
func `*`*(a: StorageDuration, b: uint32 | uint16 | uint8): StorageDuration =
StorageDuration.init(a.value * b.stuint(40))
func `+`*(a: StorageTimestamp, b: StorageDuration): StorageTimestamp =
StorageTimestamp(value: a.value + b.value)
func `+`*(a: StorageTimestamp, b: uint32 | uint16 | uint8): StorageTimestamp =
StorageTimestamp(value: a.value + b.stuint(40))
func `+`*(a: StorageDuration, b: StorageDuration): StorageDuration =
StorageDuration(value: a.value + b.value)
func `+`*(a: StorageDuration, b: uint32 | uint16 | uint8): StorageDuration =
StorageDuration(value: a.value + b.stuint(40))
func `+`*(a: ProofPeriod, b: uint32 | uint16 | uint8): ProofPeriod =
ProofPeriod(value: a.value + b.stuint(40))
func `-`*(a: StorageTimestamp, b: uint32 | uint16 | uint8): StorageTimestamp =
StorageTimestamp(value: a.value - b.stuint(40))
func `-`*(a: StorageDuration, b: StorageDuration): StorageDuration =
StorageDuration(value: a.value - b.value)
func `-`*(a: StorageDuration, b: uint32 | uint16 | uint8): StorageDuration =
StorageDuration(value: a.value - b.stuint(40))
func `-`*(a: ProofPeriod, b: uint32 | uint16 | uint8): ProofPeriod =
ProofPeriod(value: a.value - b.stuint(40))
func `+=`*(a: var StorageTimestamp, b: StorageDuration): StorageTimestamp =
a.value += b.value
func `+=`*[T: StorageDuration | StorageTimestamp](a: var T, b: T) =
a.value += b.value
func `-=`*[T: StorageDuration | StorageTimestamp](a: var T, b: T) =
a.value -= b.value
func `<`*(a, b: StorageDuration | StorageTimestamp): bool =
a.value < b.value
func `>`*(a, b: StorageDuration | StorageTimestamp): bool =
a.value > b.value
func `<=`*(a, b: StorageDuration | StorageTimestamp): bool =
a.value <= b.value
func `>=`*(a, b: StorageDuration | StorageTimestamp): bool =
a.value >= b.value
func until*(earlier, later: StorageTimestamp): StorageDuration =
doAssert earlier <= later
StorageDuration.init(later.u40 - earlier.u40)
func solidityType*(_: type StorageDuration): string =
"uint40"
func solidityType*(_: type StorageTimestamp): string =
"uint40"
func solidityType*(_: type ProofPeriod): string =
"uint40"
func encode*(encoder: var AbiEncoder, timestamp: StorageDuration) =
encoder.write(timestamp.value)
func encode*(encoder: var AbiEncoder, timestamp: StorageTimestamp) =
encoder.write(timestamp.value)
func encode*(encoder: var AbiEncoder, period: ProofPeriod) =
encoder.write(period.value)
func decode*(decoder: var AbiDecoder, T: type StorageDuration): ?!T =
let value = ?decoder.read(T.value)
success T(value: value)
func decode*(decoder: var AbiDecoder, T: type StorageTimestamp): ?!T =
let value = ?decoder.read(T.value)
success T(value: value)
func decode*(decoder: var AbiDecoder, T: type ProofPeriod): ?!T =
let value = ?decoder.read(T.value)
success T(value: value)
func `%`*(value: StorageDuration | StorageTimestamp | ProofPeriod): JsonNode =
%value.value
func fromJson*(_: type StorageDuration, json: JsonNode): ?!StorageDuration =
success StorageDuration(value: ? StUint[40].fromJson(json))
func fromJson*(_: type StorageTimestamp, json: JsonNode): ?!StorageTimestamp =
success StorageTimestamp(value: ? StUint[40].fromJson(json))
func fromJson*(_: type ProofPeriod, json: JsonNode): ?!ProofPeriod =
success ProofPeriod(value: ? StUint[40].fromJson(json))

108
codex/contracts/tokens.nim Normal file
View File

@ -0,0 +1,108 @@
import pkg/stint
import pkg/contractabi
import ../utils/json
import ./timestamps
type
TokensPerSecond* = object
value: StUint[96]
Tokens* = object
value: StUint[128]
func u256*(tokensPerSecond: TokensPerSecond): UInt256 =
tokensPerSecond.value.stuint(256)
func u256*(tokens: Tokens): UInt256 =
tokens.value.stuint(256)
func `'TokensPerSecond`*(value: static string): TokensPerSecond =
const parsed = parse(value, StUint[96])
TokensPerSecond(value: parsed)
func `'Tokens`*(value: static string): Tokens =
const parsed = parse(value, UInt128)
Tokens(value: parsed)
func init*(_: type TokensPerSecond, value: StUint[96]): TokensPerSecond =
TokensPerSecond(value: value)
func init*(_: type TokensPerSecond, value: SomeUnsignedInt): TokensPerSecond =
TokensPerSecond.init(value.stuint(96))
func init*(_: type Tokens, value: UInt128): Tokens =
Tokens(value: value)
func init*(_: type Tokens, value: SomeUnsignedInt): Tokens =
Tokens.init(value.stuint(128))
func `*`*(a: TokensPerSecond, b: SomeUnsignedInt): TokensPerSecond =
TokensPerSecond(value: a.value * b.stuint(96))
func `*`*(a: TokensPerSecond, b: StorageDuration): Tokens =
Tokens(value: a.value.stuint(128) * b.u40.stuint(128))
func `*`*(a: Tokens, b: SomeUnsignedInt): Tokens =
Tokens(value: a.value * b.stuint(128))
func `div`*(a: Tokens, b: SomeUnsignedInt): Tokens =
Tokens(value: a.value div b.stuint(128))
func `+`*(a, b: Tokens): Tokens =
Tokens(value: a.value + b.value)
func `+`*(a: Tokens, b: SomeUnsignedInt): Tokens =
Tokens(value: a.value + b.u128)
func `+`*(a: TokensPerSecond, b: SomeUnsignedInt): TokensPerSecond =
TokensPerSecond(value: a.value + b.stuint(96))
func `-`*(a, b: Tokens): Tokens =
Tokens(value: a.value - b.value)
func `+=`*[T: Tokens | TokensPerSecond](a: var T, b: T) =
a.value += b.value
func `-=`*[T: Tokens | TokensPerSecond](a: var T, b: T) =
a.value -= b.value
func `<`*(a, b: Tokens | TokensPerSecond): bool =
a.value < b.value
func `>`*(a, b: Tokens | TokensPerSecond): bool =
a.value > b.value
func `<=`*(a, b: Tokens | TokensPerSecond): bool =
a.value <= b.value
func `>=`*(a, b: Tokens | TokensPerSecond): bool =
a.value >= b.value
func solidityType*(_: type TokensPerSecond): string =
"uint96"
func solidityType*(_: type Tokens): string =
"uint128"
func encode*(encoder: var AbiEncoder, tokensPerSecond: TokensPerSecond) =
encoder.write(tokensPerSecond.value)
func encode*(encoder: var AbiEncoder, tokens: Tokens) =
encoder.write(tokens.value)
func decode*(decoder: var AbiDecoder, T: type TokensPerSecond): ?!T =
let value = ?decoder.read(T.value)
success T(value: value)
func decode*(decoder: var AbiDecoder, T: type Tokens): ?!T =
let value = ?decoder.read(T.value)
success T(value: value)
func `%`*(value: TokensPerSecond | Tokens): JsonNode =
%value.value
func fromJson*(_: type TokensPerSecond, json: JsonNode): ?!TokensPerSecond =
success TokensPerSecond(value: ? StUint[96].fromJson(json))
func fromJson*(_: type Tokens, json: JsonNode): ?!Tokens =
success Tokens(value: ? UInt128.fromJson(json))

View File

@ -4,9 +4,9 @@ import pkg/questionable
import pkg/ethers/erc20
import ./contracts/requests
import ./contracts/proofs
import ./contracts/periods
import ./clock
import ./errors
import ./periods
export chronos
export questionable
@ -22,7 +22,7 @@ type
SlotReservationNotAllowedError* = object of MarketError
Subscription* = ref object of RootObj
OnRequest* =
proc(id: RequestId, ask: StorageAsk, expiry: uint64) {.gcsafe, upraises: [].}
proc(id: RequestId, ask: StorageAsk, expiry: StorageTimestamp) {.gcsafe, upraises: [].}
OnFulfillment* = proc(requestId: RequestId) {.gcsafe, upraises: [].}
OnSlotFilled* = proc(requestId: RequestId, slotIndex: uint64) {.gcsafe, upraises: [].}
OnSlotFreed* = proc(requestId: RequestId, slotIndex: uint64) {.gcsafe, upraises: [].}
@ -37,7 +37,7 @@ type
StorageRequested* = object of MarketplaceEvent
requestId*: RequestId
ask*: StorageAsk
expiry*: StorageDuration
expiry*: StorageTimestamp
SlotFilled* = object of MarketplaceEvent
requestId* {.indexed.}: RequestId
@ -65,16 +65,19 @@ method getSigner*(
): Future[Address] {.base, async: (raises: [CancelledError, MarketError]).} =
raiseAssert("not implemented")
method zkeyHash*(market: Market): string {.base, gcsafe, raises:[].} =
raiseAssert("not implemented")
method periodicity*(market: Market): Periodicity {.base, gcsafe, raises:[].} =
raiseAssert("not implemented")
method proofTimeout*(market: Market): uint64 {.base, gcsafe, raises:[].} =
method proofTimeout*(market: Market): StorageDuration {.base, gcsafe, raises:[].} =
raiseAssert("not implemented")
method repairRewardPercentage*(market: Market): uint8 {.base, gcsafe, raises:[].} =
raiseAssert("not implemented")
method requestDurationLimit*(market: Market): uint64 {.base, gcsafe, raises:[].} =
method requestDurationLimit*(market: Market): StorageDuration {.base, gcsafe, raises:[].} =
raiseAssert("not implemented")
method proofDowntime*(market: Market): uint8 {.base, gcsafe, raises: [].} =
@ -115,12 +118,12 @@ method slotState*(
method getRequestEnd*(
market: Market, id: RequestId
): Future[SecondsSince1970] {.base, async.} =
): Future[StorageTimestamp] {.base, async.} =
raiseAssert("not implemented")
method requestExpiresAt*(
market: Market, id: RequestId
): Future[SecondsSince1970] {.base, async.} =
): Future[StorageTimestamp] {.base, async.} =
raiseAssert("not implemented")
method getHost*(
@ -130,7 +133,7 @@ method getHost*(
method currentCollateral*(
market: Market, slotId: SlotId
): Future[UInt128] {.base, async: (raises: [MarketError, CancelledError]).} =
): Future[Tokens] {.base, async: (raises: [MarketError, CancelledError]).} =
raiseAssert("not implemented")
method getActiveSlot*(market: Market, slotId: SlotId): Future[?Slot] {.base, async.} =
@ -141,7 +144,7 @@ method fillSlot*(
requestId: RequestId,
slotIndex: uint64,
proof: Groth16Proof,
collateral: UInt128,
collateral: Tokens,
) {.base, async: (raises: [CancelledError, MarketError]).} =
raiseAssert("not implemented")
@ -177,12 +180,12 @@ method submitProof*(
raiseAssert("not implemented")
method markProofAsMissing*(
market: Market, id: SlotId, period: Period
market: Market, id: SlotId, period: ProofPeriod
) {.base, async: (raises: [CancelledError, MarketError]).} =
raiseAssert("not implemented")
method canProofBeMarkedAsMissing*(
market: Market, id: SlotId, period: Period
market: Market, id: SlotId, period: ProofPeriod
): Future[bool] {.base, async.} =
raiseAssert("not implemented")

View File

@ -494,7 +494,7 @@ proc setupRequest(
nodes: uint,
tolerance: uint,
pricePerBytePerSecond: TokensPerSecond,
collateralPerByte: UInt128,
collateralPerByte: Tokens,
expiry: StorageDuration,
): Future[?!StorageRequest] {.async.} =
## Setup slots for a given dataset
@ -575,7 +575,7 @@ proc requestStorage*(
nodes: uint,
tolerance: uint,
pricePerBytePerSecond: TokensPerSecond,
collateralPerByte: UInt128,
collateralPerByte: Tokens,
expiry: StorageDuration,
): Future[?!PurchaseId] {.async.} =
## Initiate a request for storage sequence, this might
@ -614,6 +614,7 @@ proc requestStorage*(
proc onStore(
self: CodexNodeRef,
request: StorageRequest,
expiry: StorageTimestamp,
slotIdx: uint64,
blocksCb: BlocksCb,
isRepairing: bool = false,
@ -642,8 +643,6 @@ proc onStore(
trace "Unable to create slots builder", err = err.msg
return failure(err)
let expiry = request.expiry
if slotIdx > manifest.slotRoots.high.uint64:
trace "Slot index not in manifest", slotIdx
return failure(newException(CodexError, "Slot index not in manifest"))
@ -778,11 +777,12 @@ proc start*(self: CodexNodeRef) {.async.} =
if hostContracts =? self.contracts.host:
hostContracts.sales.onStore = proc(
request: StorageRequest,
expiry: StorageTimestamp,
slot: uint64,
onBatch: BatchProc,
isRepairing: bool = false,
): Future[?!void] =
self.onStore(request, slot, onBatch, isRepairing)
self.onStore(request, expiry, slot, onBatch, isRepairing)
hostContracts.sales.onExpiryUpdate = proc(
rootCid: Cid, expiry: SecondsSince1970

View File

@ -1,17 +0,0 @@
import pkg/stint
type
Periodicity* = object
seconds*: uint64
Period* = uint64
Timestamp* = uint64
func periodOf*(periodicity: Periodicity, timestamp: Timestamp): Period =
timestamp div periodicity.seconds
func periodStart*(periodicity: Periodicity, period: Period): Timestamp =
period * periodicity.seconds
func periodEnd*(periodicity: Periodicity, period: Period): Timestamp =
periodicity.periodStart(period + 1)

View File

@ -34,9 +34,10 @@ method run*(
var ended: Future[void]
try:
let subscription = await market.subscribeRequestFailed(purchase.requestId, callback)
let requestEnd = await market.getRequestEnd(purchase.requestId)
# Ensure that we're past the request end by waiting an additional second
ended = clock.waitUntil((await market.getRequestEnd(purchase.requestId)) + 1)
ended = clock.waitUntil(requestEnd.toSecondsSince1970 + 1)
let fut = await one(ended, failed)
await subscription.unsubscribe()
if fut.id == failed.id:

View File

@ -39,9 +39,9 @@ method run*(
await subscription.unsubscribe()
proc withTimeout(future: Future[void]) {.async.} =
let expiry = (await market.requestExpiresAt(request.id)) + 1
let expiry = (await market.requestExpiresAt(request.id)) + 1'u8
trace "waiting for request fulfillment or expiry", expiry
await future.withTimeout(clock, expiry)
await future.withTimeout(clock, expiry.toSecondsSince1970)
try:
await wait().withTimeout()

View File

@ -489,7 +489,7 @@ proc initSalesApi(node: CodexNodeRef, router: var RestRouter) =
restAv.minPricePerBytePerSecond,
restAv.totalCollateral,
enabled = restAv.enabled |? true,
until = restAv.until |? 0,
until = restAv.until |? 0'StorageTimestamp,
)
), error:
if error of CancelledError:
@ -681,7 +681,7 @@ proc initPurchasingApi(node: CodexNodeRef, router: var RestRouter) =
let expiry = params.expiry
if expiry <= 0 or expiry >= params.duration:
if expiry == 0'StorageDuration or expiry >= params.duration:
return RestApiResponse.error(
Http422,
"Expiry must be greater than zero and less than the request's duration",
@ -693,12 +693,12 @@ proc initPurchasingApi(node: CodexNodeRef, router: var RestRouter) =
Http422, "Proof probability must be greater than zero", headers = headers
)
if params.collateralPerByte <= 0:
if params.collateralPerByte <= 0'Tokens:
return RestApiResponse.error(
Http422, "Collateral per byte must be greater than zero", headers = headers
)
if params.pricePerBytePerSecond <= 0:
if params.pricePerBytePerSecond <= 0'TokensPerSecond:
return RestApiResponse.error(
Http422,
"Price per byte per second must be greater than zero",
@ -706,7 +706,7 @@ proc initPurchasingApi(node: CodexNodeRef, router: var RestRouter) =
)
let requestDurationLimit = contracts.purchasing.market.requestDurationLimit
if params.duration.u64 > requestDurationLimit:
if params.duration > requestDurationLimit:
return RestApiResponse.error(
Http422,
"Duration exceeds limit of " & $requestDurationLimit & " seconds",

View File

@ -16,7 +16,7 @@ type
duration* {.serialize.}: StorageDuration
proofProbability* {.serialize.}: UInt256
pricePerBytePerSecond* {.serialize.}: TokensPerSecond
collateralPerByte* {.serialize.}: UInt128
collateralPerByte* {.serialize.}: Tokens
expiry* {.serialize.}: StorageDuration
nodes* {.serialize.}: ?uint
tolerance* {.serialize.}: ?uint
@ -29,12 +29,12 @@ type
RestAvailability* = object
totalSize* {.serialize.}: uint64
duration* {.serialize.}: uint64
minPricePerBytePerSecond* {.serialize.}: UInt256
totalCollateral* {.serialize.}: UInt256
duration* {.serialize.}: StorageDuration
minPricePerBytePerSecond* {.serialize.}: TokensPerSecond
totalCollateral* {.serialize.}: Tokens
freeSize* {.serialize.}: ?uint64
enabled* {.serialize.}: ?bool
until* {.serialize.}: ?SecondsSince1970
until* {.serialize.}: ?StorageTimestamp
RestSalesAgent* = object
state* {.serialize.}: string

View File

@ -114,7 +114,7 @@ proc cleanUp(
sales: Sales,
agent: SalesAgent,
reprocessSlot: bool,
returnedCollateral: ?UInt256,
returnedCollateral: ?Tokens,
processing: Future[void],
) {.async.} =
let data = agent.data
@ -186,7 +186,7 @@ proc processSlot(sales: Sales, item: SlotQueueItem, done: Future[void]) =
)
agent.onCleanUp = proc(
reprocessSlot = false, returnedCollateral = UInt256.none
reprocessSlot = false, returnedCollateral = Tokens.none
) {.async.} =
await sales.cleanUp(agent, reprocessSlot, returnedCollateral, done)
@ -254,7 +254,7 @@ proc load*(sales: Sales) {.async.} =
newSalesAgent(sales.context, slot.request.id, slot.slotIndex, some slot.request)
agent.onCleanUp = proc(
reprocessSlot = false, returnedCollateral = UInt256.none
reprocessSlot = false, returnedCollateral = Tokens.none
) {.async.} =
# 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
@ -282,7 +282,7 @@ proc OnAvailabilitySaved(
queue.unpause()
proc onStorageRequested(
sales: Sales, requestId: RequestId, ask: StorageAsk, expiry: uint64
sales: Sales, requestId: RequestId, ask: StorageAsk, expiry: StorageTimestamp
) {.raises: [].} =
logScope:
topics = "marketplace sales onStorageRequested"
@ -294,7 +294,7 @@ proc onStorageRequested(
trace "storage requested, adding slots to queue"
let collateral = ask.collateralPerSlot().stuint(256)
let collateral = ask.collateralPerSlot()
without items =? SlotQueueItem.init(requestId, ask, expiry, collateral).catch, err:
if err of SlotsOutOfRangeError:
@ -331,9 +331,9 @@ proc onSlotFreed(sales: Sales, requestId: RequestId, slotIndex: uint64) =
error "unknown request in contract", error = err.msgDetail
return
let collateral = request.ask.collateralPerSlot.stuint(256)
let collateral = request.ask.collateralPerSlot
let percentage = context.market.repairRewardPercentage
let repairReward = (collateral * percentage.u256) div 100.u256
let repairReward = (collateral * percentage) div 100'u
if slotIndex > uint16.high.uint64:
error "Cannot cast slot index to uint16, value = ", slotIndex
@ -365,7 +365,7 @@ proc subscribeRequested(sales: Sales) {.async.} =
let market = context.market
proc onStorageRequested(
requestId: RequestId, ask: StorageAsk, expiry: uint64
requestId: RequestId, ask: StorageAsk, expiry: StorageTimestamp
) {.raises: [].} =
sales.onStorageRequested(requestId, ask, expiry)

View File

@ -14,17 +14,17 @@
## |---------------------------------------------------| |--------------------------------------|
## | AvailabilityId | id | PK |<-||-------o<-| AvailabilityId | availabilityId | FK |
## |---------------------------------------------------| |--------------------------------------|
## | UInt256 | totalSize | | | UInt256 | size | |
## | uint64 | totalSize | | | uint64 | size | |
## |---------------------------------------------------| |--------------------------------------|
## | UInt256 | freeSize | | | UInt256 | slotIndex | |
## | uint64 | freeSize | | | uint64 | slotIndex | |
## |---------------------------------------------------| +--------------------------------------+
## | UInt256 | duration | |
## | StorageDuration | duration | |
## |---------------------------------------------------|
## | UInt256 | minPricePerBytePerSecond | |
## | TokensPerSecond | minPricePerBytePerSecond | |
## |---------------------------------------------------|
## | UInt256 | totalCollateral | |
## | Tokens | totalCollateral | |
## |---------------------------------------------------|
## | UInt256 | totalRemainingCollateral | |
## | Tokens | totalRemainingCollateral | |
## +---------------------------------------------------+
import pkg/upraises
@ -34,7 +34,6 @@ push:
import std/sequtils
import std/sugar
import std/typetraits
import std/sequtils
import std/times
import pkg/chronos
import pkg/datastore
@ -67,16 +66,16 @@ type
id* {.serialize.}: AvailabilityId
totalSize* {.serialize.}: uint64
freeSize* {.serialize.}: uint64
duration* {.serialize.}: uint64
minPricePerBytePerSecond* {.serialize.}: UInt256
totalCollateral {.serialize.}: UInt256
totalRemainingCollateral* {.serialize.}: UInt256
duration* {.serialize.}: StorageDuration
minPricePerBytePerSecond* {.serialize.}: TokensPerSecond
totalCollateral {.serialize.}: Tokens
totalRemainingCollateral* {.serialize.}: Tokens
# If set to false, the availability will not accept new slots.
# If enabled, it will not impact any existing slots that are already being hosted.
enabled* {.serialize.}: bool
# Specifies the latest timestamp after which the availability will no longer host any slots.
# If set to 0, there will be no restrictions.
until* {.serialize.}: SecondsSince1970
until* {.serialize.}: StorageTimestamp
Reservation* = ref object
id* {.serialize.}: ReservationId
@ -84,7 +83,7 @@ type
size* {.serialize.}: uint64
requestId* {.serialize.}: RequestId
slotIndex* {.serialize.}: uint64
validUntil* {.serialize.}: SecondsSince1970
validUntil* {.serialize.}: StorageTimestamp
Reservations* = ref object of RootObj
availabilityLock: AsyncLock
@ -144,11 +143,11 @@ proc init*(
_: type Availability,
totalSize: uint64,
freeSize: uint64,
duration: uint64,
minPricePerBytePerSecond: UInt256,
totalCollateral: UInt256,
duration: StorageDuration,
minPricePerBytePerSecond: TokensPerSecond,
totalCollateral: Tokens,
enabled: bool,
until: SecondsSince1970,
until: StorageTimestamp,
): Availability =
var id: array[32, byte]
doAssert randomBytes(id) == 32
@ -164,10 +163,10 @@ proc init*(
until: until,
)
func totalCollateral*(self: Availability): UInt256 {.inline.} =
func totalCollateral*(self: Availability): Tokens {.inline.} =
return self.totalCollateral
proc `totalCollateral=`*(self: Availability, value: UInt256) {.inline.} =
proc `totalCollateral=`*(self: Availability, value: Tokens) {.inline.} =
self.totalCollateral = value
self.totalRemainingCollateral = value
@ -177,7 +176,7 @@ proc init*(
size: uint64,
requestId: RequestId,
slotIndex: uint64,
validUntil: SecondsSince1970,
validUntil: StorageTimestamp,
): Reservation =
var id: array[32, byte]
doAssert randomBytes(id) == 32
@ -230,8 +229,8 @@ func key*(reservationId: ReservationId, availabilityId: AvailabilityId): ?!Key =
func key*(availability: Availability): ?!Key =
return availability.id.key
func maxCollateralPerByte*(availability: Availability): UInt256 =
return availability.totalRemainingCollateral div availability.freeSize.stuint(256)
func maxCollateralPerByte*(availability: Availability): Tokens =
return availability.totalRemainingCollateral div availability.freeSize
func key*(reservation: Reservation): ?!Key =
return key(reservation.id, reservation.availabilityId)
@ -295,11 +294,6 @@ proc updateAvailability(
logScope:
availabilityId = obj.id
if obj.until < 0:
let error =
newException(UntilOutOfBoundsError, "Cannot set until to a negative value")
return failure(error)
without key =? obj.key, error:
return failure(error)
@ -314,7 +308,7 @@ proc updateAvailability(
else:
return failure(err)
if obj.until > 0:
if obj.until > 0'StorageTimestamp:
without allReservations =? await self.all(Reservation, obj.id), error:
error.msg = "Error updating reservation: " & error.msg
return failure(error)
@ -383,7 +377,7 @@ proc deleteReservation*(
self: Reservations,
reservationId: ReservationId,
availabilityId: AvailabilityId,
returnedCollateral: ?UInt256 = UInt256.none,
returnedCollateral = Tokens.none,
): Future[?!void] {.async.} =
logScope:
reservationId
@ -429,20 +423,15 @@ proc deleteReservation*(
proc createAvailability*(
self: Reservations,
size: uint64,
duration: uint64,
minPricePerBytePerSecond: UInt256,
totalCollateral: UInt256,
duration: StorageDuration,
minPricePerBytePerSecond: TokensPerSecond,
totalCollateral: Tokens,
enabled: bool,
until: SecondsSince1970,
until: StorageTimestamp,
): Future[?!Availability] {.async.} =
trace "creating availability",
size, duration, minPricePerBytePerSecond, totalCollateral, enabled, until
if until < 0:
let error =
newException(UntilOutOfBoundsError, "Cannot set until to a negative value")
return failure(error)
let availability = Availability.init(
size, size, duration, minPricePerBytePerSecond, totalCollateral, enabled, until
)
@ -468,8 +457,8 @@ method createReservation*(
slotSize: uint64,
requestId: RequestId,
slotIndex: uint64,
collateralPerByte: UInt256,
validUntil: SecondsSince1970,
collateralPerByte: Tokens,
validUntil: StorageTimestamp,
): Future[?!Reservation] {.async, base.} =
withLock(self.availabilityLock):
without availabilityKey =? availabilityId.key, error:
@ -500,7 +489,7 @@ method createReservation*(
availability.freeSize -= slotSize
# adjust the remaining totalRemainingCollateral
availability.totalRemainingCollateral -= slotSize.u256 * collateralPerByte
availability.totalRemainingCollateral -= collateralPerByte * slotSize
# update availability with reduced size
trace "Updating availability with reduced size"
@ -695,9 +684,11 @@ proc all*(
proc findAvailability*(
self: Reservations,
size, duration: uint64,
pricePerBytePerSecond, collateralPerByte: UInt256,
validUntil: SecondsSince1970,
size: uint64,
duration: StorageDuration,
pricePerBytePerSecond: TokensPerSecond,
collateralPerByte: Tokens,
validUntil: StorageTimestamp,
): Future[?Availability] {.async.} =
without storables =? (await self.storables(Availability)), e:
error "failed to get all storables", error = e.msg
@ -709,7 +700,7 @@ proc findAvailability*(
duration <= availability.duration and
collateralPerByte <= availability.maxCollateralPerByte and
pricePerBytePerSecond >= availability.minPricePerBytePerSecond and
(availability.until == 0 or availability.until >= validUntil):
(availability.until == 0'StorageTimestamp or availability.until >= validUntil):
trace "availability matched",
id = availability.id,
enabled = availability.enabled,

View File

@ -28,7 +28,7 @@ type
onFilled*: ?OnFilled
OnCleanUp* = proc(
reprocessSlot = false, returnedCollateral = UInt256.none
reprocessSlot = false, returnedCollateral = Tokens.none
): Future[void] {.gcsafe, upraises: [].}
OnFilled* = proc(request: StorageRequest, slotIndex: uint64) {.gcsafe, upraises: [].}
@ -82,7 +82,7 @@ proc subscribeCancellation(agent: SalesAgent) {.async.} =
try:
let market = agent.context.market
let expiry = await market.requestExpiresAt(data.requestId)
let expiry = (await market.requestExpiresAt(data.requestId)).toSecondsSince1970
while true:
let deadline = max(clock.now, expiry) + 1

View File

@ -26,7 +26,11 @@ type
BlocksCb* = proc(blocks: seq[bt.Block]): Future[?!void] {.gcsafe, raises: [].}
OnStore* = proc(
request: StorageRequest, slot: uint64, blocksCb: BlocksCb, isRepairing: bool
request: StorageRequest,
expiry: StorageTimestamp,
slot: uint64,
blocksCb: BlocksCb,
isRepairing: bool
): Future[?!void] {.gcsafe, upraises: [].}
OnProve* = proc(slot: Slot, challenge: ProofChallenge): Future[?!Groth16Proof] {.
gcsafe, upraises: []

View File

@ -32,11 +32,11 @@ type
requestId: RequestId
slotIndex: uint16
slotSize: uint64
duration: uint64
pricePerBytePerSecond: UInt256
repairReward: UInt256
collateral: UInt256
expiry: uint64
duration: StorageDuration
pricePerBytePerSecond: TokensPerSecond
repairReward: Tokens
collateral: Tokens
expiry: ?StorageTimestamp
seen: bool
# don't need to -1 to prevent overflow when adding 1 (to always allow push)
@ -70,14 +70,14 @@ const DefaultMaxWorkers = 3
# slots.
const DefaultMaxSize = 128'u16
proc profitability(item: SlotQueueItem): UInt256 =
proc profitability(item: SlotQueueItem): Tokens =
let price =
StorageAsk(
duration: item.duration.stuint(40),
pricePerBytePerSecond: item.pricePerBytePerSecond.stuint(96),
duration: item.duration,
pricePerBytePerSecond: item.pricePerBytePerSecond,
slotSize: item.slotSize,
).pricePerSlot
return price.stuint(256) + item.repairReward
return price + item.repairReward
proc `<`*(a, b: SlotQueueItem): bool =
# for A to have a higher priority than B (in a min queue), A must be less than
@ -98,8 +98,9 @@ proc `<`*(a, b: SlotQueueItem): bool =
scoreA.addIf(a.collateral < b.collateral, 2)
scoreB.addIf(a.collateral > b.collateral, 2)
scoreA.addIf(a.expiry > b.expiry, 1)
scoreB.addIf(a.expiry < b.expiry, 1)
if expiryA =? a.expiry and expiryB =? b.expiry:
scoreA.addIf(expiryA > expiryB, 1)
scoreB.addIf(expiryA < expiryB, 1)
return scoreA > scoreB
@ -141,22 +142,22 @@ proc init(_: type SlotQueueWorker): SlotQueueWorker =
SlotQueueWorker(doneProcessing: workerFut)
proc init*(
proc init(
_: type SlotQueueItem,
requestId: RequestId,
slotIndex: uint16,
ask: StorageAsk,
expiry: uint64,
collateral: UInt256,
repairReward = 0.u256,
expiry: ?StorageTimestamp,
collateral: Tokens,
repairReward = 0'Tokens,
seen = false,
): SlotQueueItem =
SlotQueueItem(
requestId: requestId,
slotIndex: slotIndex,
slotSize: ask.slotSize,
duration: ask.duration.u64,
pricePerBytePerSecond: ask.pricePerBytePerSecond.stuint(256),
duration: ask.duration,
pricePerBytePerSecond: ask.pricePerBytePerSecond,
collateral: collateral,
expiry: expiry,
seen: seen,
@ -164,22 +165,39 @@ proc init*(
proc init*(
_: type SlotQueueItem,
request: StorageRequest,
requestId: RequestId,
slotIndex: uint16,
collateral: UInt256,
repairReward = 0.u256,
ask: StorageAsk,
expiry: StorageTimestamp,
collateral: Tokens,
repairReward = 0'Tokens,
seen = false,
): SlotQueueItem =
SlotQueueItem.init(
request.id, slotIndex, request.ask, request.expiry.u64, collateral, repairReward
)
SlotQueueItem.init(requestId, slotIndex, ask, some expiry, collateral, repairReward, seen)
proc init*(
_: type SlotQueueItem,
request: StorageRequest,
slotIndex: uint16,
collateral: Tokens,
repairReward = 0'Tokens,
): SlotQueueItem =
SlotQueueItem(
requestId: request.id,
slotIndex: slotIndex,
slotSize: request.ask.slotSize,
duration: request.ask.duration,
pricePerBytePerSecond: request.ask.pricePerBytePerSecond,
collateral: collateral
)
proc init(
_: type SlotQueueItem,
requestId: RequestId,
ask: StorageAsk,
expiry: uint64,
collateral: UInt256,
repairReward = 0.u256,
expiry: ?StorageTimestamp,
collateral: Tokens,
repairReward = 0'Tokens,
): seq[SlotQueueItem] {.raises: [SlotsOutOfRangeError].} =
if not ask.slots.inRange:
raise newException(SlotsOutOfRangeError, "Too many slots")
@ -194,14 +212,24 @@ proc init*(
Rng.instance.shuffle(items)
return items
proc init*(
_: type SlotQueueItem,
requestId: RequestId,
ask: StorageAsk,
expiry: StorageTimestamp,
collateral: Tokens,
repairReward = 0'Tokens,
): seq[SlotQueueItem] {.raises: [SlotsOutOfRangeError].} =
SlotQueueItem.init(requestId, ask, some expiry, collateral, repairReward)
proc init*(
_: type SlotQueueItem,
request: StorageRequest,
collateral: UInt256,
repairReward = 0.u256,
): seq[SlotQueueItem] =
collateral: Tokens,
repairReward = 0'Tokens,
): seq[SlotQueueItem] {.raises: [SlotsOutOfRangeError].} =
return SlotQueueItem.init(
request.id, request.ask, request.expiry.u64, collateral, repairReward
request.id, request.ask, StorageTimestamp.none, collateral, repairReward
)
proc inRange*(val: SomeUnsignedInt): bool =
@ -216,13 +244,13 @@ proc slotIndex*(self: SlotQueueItem): uint16 =
proc slotSize*(self: SlotQueueItem): uint64 =
self.slotSize
proc duration*(self: SlotQueueItem): uint64 =
proc duration*(self: SlotQueueItem): StorageDuration =
self.duration
proc pricePerBytePerSecond*(self: SlotQueueItem): UInt256 =
proc pricePerBytePerSecond*(self: SlotQueueItem): TokensPerSecond =
self.pricePerBytePerSecond
proc collateralPerByte*(self: SlotQueueItem): UInt256 =
proc collateralPerByte*(self: SlotQueueItem): Tokens =
self.collateralPerByte
proc seen*(self: SlotQueueItem): bool =

View File

@ -31,7 +31,7 @@ method run*(
raiseAssert "no sale request"
try:
var returnedCollateral = UInt256.none
var returnedCollateral = Tokens.none
if await slotIsFilledByMe(market, data.requestId, data.slotIndex):
debug "Collecting collateral and partial payout",
@ -53,7 +53,7 @@ method run*(
if onCleanUp =? agent.onCleanUp:
await onCleanUp(
reprocessSlot = false,
returnedCollateral = some currentCollateral.stuint(256),
returnedCollateral = returnedCollateral,
)
warn "Sale cancelled due to timeout",

View File

@ -38,6 +38,7 @@ method run*(
let agent = SalesAgent(machine)
let data = agent.data
let context = agent.context
let market = context.market
let reservations = context.reservations
without onStore =? context.onStore:
@ -67,11 +68,20 @@ method run*(
return await reservations.release(reservation.id, reservation.availabilityId, bytes)
try:
let slotId = slotId(request.id, data.slotIndex)
let isRepairing = (await context.market.slotState(slotId)) == SlotState.Repair
let requestId = request.id
let slotId = slotId(requestId, data.slotIndex)
let requestState = await market.requestState(requestId)
let isRepairing = (await market.slotState(slotId)) == SlotState.Repair
trace "Retrieving expiry"
var expiry: StorageTimestamp
if state =? requestState and state == RequestState.Started:
expiry = await market.getRequestEnd(requestId)
else:
expiry = await market.requestExpiresAt(requestId)
trace "Starting download"
if err =? (await onStore(request, data.slotIndex, onBlocks, isRepairing)).errorOption:
if err =? (await onStore(request, expiry, data.slotIndex, onBlocks, isRepairing)).errorOption:
return some State(SaleErrored(error: err, reprocessSlot: false))
trace "Download complete"

View File

@ -55,7 +55,7 @@ method run*(
without onExpiryUpdate =? context.onExpiryUpdate:
raiseAssert "onExpiryUpdate callback not set"
let requestEnd = await market.getRequestEnd(data.requestId)
let requestEnd = (await market.getRequestEnd(data.requestId)).toSecondsSince1970
if err =? (await onExpiryUpdate(request.content.cid, requestEnd)).errorOption:
return some State(SaleErrored(error: err))

View File

@ -12,7 +12,7 @@ logScope:
topics = "marketplace sales finished"
type SaleFinished* = ref object of SaleState
returnedCollateral*: ?UInt256
returnedCollateral*: ?Tokens
method `$`*(state: SaleFinished): string =
"SaleFinished"

View File

@ -25,9 +25,9 @@ method onFailed*(state: SaleInitialProving, request: StorageRequest): ?State =
proc waitUntilNextPeriod(clock: Clock, periodicity: Periodicity) {.async.} =
trace "Waiting until next period"
let period = periodicity.periodOf(clock.now().Timestamp)
let period = periodicity.periodOf(clock.now())
let periodEnd = periodicity.periodEnd(period)
await clock.waitUntil((periodEnd + 1).toSecondsSince1970)
await clock.waitUntil((periodEnd + 1'u8).toSecondsSince1970)
proc waitForStableChallenge(market: Market, clock: Clock, slotId: SlotId) {.async.} =
let periodicity = market.periodicity

View File

@ -73,10 +73,10 @@ method run*(
without availability =?
await reservations.findAvailability(
request.ask.slotSize,
request.ask.duration.u64,
request.ask.pricePerBytePerSecond.stuint(256),
request.ask.collateralPerByte.stuint(256),
requestEnd.u64
request.ask.duration,
request.ask.pricePerBytePerSecond,
request.ask.collateralPerByte,
requestEnd
):
debug "No availability found for request, ignoring"
@ -90,8 +90,8 @@ method run*(
request.ask.slotSize,
request.id,
data.slotIndex,
request.ask.collateralPerByte.stuint(256),
requestEnd.u64,
request.ask.collateralPerByte,
requestEnd
), error:
trace "Creation of reservation failed"
# Race condition:

View File

@ -26,7 +26,7 @@ method prove*(
challenge: ProofChallenge,
onProve: OnProve,
market: Market,
currentPeriod: Period,
currentPeriod: ProofPeriod,
) {.base, async.} =
try:
without proof =? (await onProve(slot, challenge)), err:
@ -58,14 +58,14 @@ proc proveLoop(
slotIndex
slotId = slot.id
proc getCurrentPeriod(): Period =
proc getCurrentPeriod(): ProofPeriod =
let periodicity = market.periodicity
return periodicity.periodOf(clock.now().Timestamp)
return periodicity.periodOf(clock.now())
proc waitUntilPeriod(period: Period) {.async.} =
proc waitUntilPeriod(period: ProofPeriod) {.async.} =
let periodicity = market.periodicity
# Ensure that we're past the period boundary by waiting an additional second
await clock.waitUntil((periodicity.periodStart(period) + 1).toSecondsSince1970)
await clock.waitUntil((periodicity.periodStart(period) + 1'u8).toSecondsSince1970)
while true:
let currentPeriod = getCurrentPeriod()
@ -97,7 +97,7 @@ proc proveLoop(
raise newException(SlotNotFilledError, message)
debug "waiting until next period"
await waitUntilPeriod(currentPeriod + 1)
await waitUntilPeriod(currentPeriod + 1'u8)
method `$`*(state: SaleProving): string =
"SaleProving"

View File

@ -20,7 +20,7 @@ when codex_enable_proof_failures:
failEveryNProofs*: int
proofCount: int
proc onSubmitProofError(error: ref CatchableError, period: Period, slotId: SlotId) =
proc onSubmitProofError(error: ref CatchableError, period: ProofPeriod, slotId: SlotId) =
error "Submitting invalid proof failed", period, slotId, msg = error.msgDetail
method prove*(
@ -29,7 +29,7 @@ when codex_enable_proof_failures:
challenge: ProofChallenge,
onProve: OnProve,
market: Market,
currentPeriod: Period,
currentPeriod: ProofPeriod,
) {.async.} =
try:
trace "Processing proving in simulated mode"

View File

@ -34,8 +34,8 @@ proc new*(
proc slots*(validation: Validation): seq[SlotId] =
validation.slots.toSeq
proc getCurrentPeriod(validation: Validation): Period =
return validation.periodicity.periodOf(validation.clock.now().Timestamp)
proc getCurrentPeriod(validation: Validation): ProofPeriod =
return validation.periodicity.periodOf(validation.clock.now())
proc waitUntilNextPeriod(validation: Validation) {.async.} =
let period = validation.getCurrentPeriod()
@ -79,7 +79,7 @@ proc removeSlotsThatHaveEnded(validation: Validation) {.async.} =
validation.slots.excl(ended)
proc markProofAsMissing(
validation: Validation, slotId: SlotId, period: Period
validation: Validation, slotId: SlotId, period: ProofPeriod
) {.async.} =
logScope:
currentPeriod = validation.getCurrentPeriod()
@ -99,7 +99,7 @@ proc markProofAsMissing(
proc markProofsAsMissing(validation: Validation) {.async.} =
let slots = validation.slots
for slotId in slots:
let previousPeriod = validation.getCurrentPeriod() - 1
let previousPeriod = validation.getCurrentPeriod() - 1'u8
await validation.markProofAsMissing(slotId, previousPeriod)
proc run(validation: Validation) {.async: (raises: []).} =
@ -121,7 +121,7 @@ proc findEpoch(validation: Validation, secondsAgo: uint64): SecondsSince1970 =
proc restoreHistoricalState(validation: Validation) {.async.} =
trace "Restoring historical state..."
let requestDurationLimit = validation.market.requestDurationLimit
let startTimeEpoch = validation.findEpoch(secondsAgo = requestDurationLimit)
let startTimeEpoch = validation.findEpoch(secondsAgo = requestDurationLimit.u64)
let slotFilledEvents =
await validation.market.queryPastSlotFilledEvents(fromTime = startTimeEpoch)
for event in slotFilledEvents:
@ -138,7 +138,7 @@ proc start*(validation: Validation) {.async.} =
trace "Starting validator",
groups = validation.config.groups, groupIndex = validation.config.groupIndex
validation.periodicity = validation.market.periodicity
validation.proofTimeout = validation.market.proofTimeout
validation.proofTimeout = validation.market.proofTimeout.u64
await validation.subscribeSlotFilled()
await validation.restoreHistoricalState()
validation.running = validation.run()

View File

@ -66,17 +66,17 @@ proc example*(_: type MultiHash, mcodec = Sha256HashCodec): MultiHash =
MultiHash.digest($mcodec, bytes).tryGet()
proc example*(
_: type Availability, collateralPerByte = uint8.example.u256
_: type Availability, collateralPerByte = Tokens.init(uint8.example)
): Availability =
let totalSize = uint16.example.uint64
Availability.init(
totalSize = totalSize,
freeSize = uint16.example.uint64,
duration = uint16.example.uint64,
minPricePerBytePerSecond = uint8.example.u256,
totalCollateral = totalSize.u256 * collateralPerByte,
duration = StorageDuration.init(uint16.example),
minPricePerBytePerSecond = TokensPerSecond.init(uint8.example),
totalCollateral = collateralPerByte * totalSize,
enabled = true,
until = 0.SecondsSince1970,
until = 0'StorageTimestamp,
)
proc example*(_: type Reservation): Reservation =

View File

@ -14,6 +14,7 @@ from pkg/ethers import BlockTag
import codex/clock
import ../examples
import ./mockclock
export market
export tables
@ -27,8 +28,8 @@ type
activeRequests*: Table[Address, seq[RequestId]]
activeSlots*: Table[Address, seq[SlotId]]
requested*: seq[StorageRequest]
requestEnds*: Table[RequestId, SecondsSince1970]
requestExpiry*: Table[RequestId, SecondsSince1970]
requestEnds*: Table[RequestId, StorageTimestamp]
requestExpiry*: Table[RequestId, StorageTimestamp]
requestState*: Table[RequestId, RequestState]
slotState*: Table[SlotId, SlotState]
fulfilled*: seq[Fulfillment]
@ -51,7 +52,7 @@ type
errorOnFillSlot*: ?(ref MarketError)
errorOnFreeSlot*: ?(ref MarketError)
errorOnGetHost*: ?(ref MarketError)
clock: ?Clock
clock: Clock
Fulfillment* = object
requestId*: RequestId
@ -63,8 +64,8 @@ type
host*: Address
slotIndex*: uint64
proof*: Groth16Proof
timestamp: ?SecondsSince1970
collateral*: UInt128
timestamp: SecondsSince1970
collateral*: Tokens
Subscriptions = object
onRequest: seq[RequestSubscription]
@ -113,7 +114,7 @@ proc hash*(address: Address): Hash =
proc hash*(requestId: RequestId): Hash =
hash(requestId.toArray)
proc new*(_: type MockMarket, clock: ?Clock = Clock.none): MockMarket =
proc new*(_: type MockMarket, clock: Clock = MockClock.new()): MockMarket =
## Create a new mocked Market instance
##
let config = MarketplaceConfig(
@ -124,13 +125,13 @@ proc new*(_: type MockMarket, clock: ?Clock = Clock.none): MockMarket =
validatorRewardPercentage: 20,
),
proofs: ProofConfig(
period: 10.stuint(40),
timeout: 5.stuint(40),
period: 10'StorageDuration,
timeout: 5'StorageDuration,
downtime: 64.uint8,
downtimeProduct: 67.uint8,
),
reservations: SlotReservationsConfig(maxReservations: 3),
requestDurationLimit: (60 * 60 * 24 * 30).stuint(40),
requestDurationLimit: StorageDuration.init((60 * 60 * 24 * 30).stuint(40)),
)
MockMarket(
signer: Address.example, config: config, canReserveSlot: true, clock: clock
@ -142,13 +143,13 @@ method getSigner*(
return market.signer
method periodicity*(mock: MockMarket): Periodicity =
return Periodicity(seconds: mock.config.proofs.period.u64)
return Periodicity(seconds: mock.config.proofs.period)
method proofTimeout*(market: MockMarket): uint64 =
return market.config.proofs.timeout.u64
method proofTimeout*(market: MockMarket): StorageDuration =
return market.config.proofs.timeout
method requestDurationLimit*(market: MockMarket): uint64 =
return market.config.requestDurationLimit.u64
method requestDurationLimit*(market: MockMarket): StorageDuration =
return market.config.requestDurationLimit
method proofDowntime*(market: MockMarket): uint8 =
return market.config.proofs.downtime
@ -162,10 +163,15 @@ method getPointer*(market: MockMarket, slotId: SlotId): Future[uint8] {.async.}
method requestStorage*(
market: MockMarket, request: StorageRequest
) {.async: (raises: [CancelledError, MarketError]).} =
let now = StorageTimestamp.init(market.clock.now())
let requestExpiresAt = now + request.expiry
let requestEndsAt = now + request.ask.duration
market.requested.add(request)
market.requestExpiry[request.id] = requestExpiresAt
market.requestEnds[request.id] = requestEndsAt
var subscriptions = market.subscriptions.onRequest
for subscription in subscriptions:
subscription.callback(request.id, request.ask, request.expiry.u64)
subscription.callback(request.id, request.ask, requestExpiresAt)
method myRequests*(market: MockMarket): Future[seq[RequestId]] {.async.} =
return market.activeRequests[market.signer]
@ -206,12 +212,12 @@ method slotState*(
method getRequestEnd*(
market: MockMarket, id: RequestId
): Future[SecondsSince1970] {.async.} =
): Future[StorageTimestamp] {.async.} =
return market.requestEnds[id]
method requestExpiresAt*(
market: MockMarket, id: RequestId
): Future[SecondsSince1970] {.async.} =
): Future[StorageTimestamp] {.async.} =
return market.requestExpiry[id]
method getHost*(
@ -227,11 +233,11 @@ method getHost*(
method currentCollateral*(
market: MockMarket, slotId: SlotId
): Future[UInt128] {.async: (raises: [MarketError, CancelledError]).} =
): Future[Tokens] {.async: (raises: [MarketError, CancelledError]).} =
for slot in market.filled:
if slotId == slotId(slot.requestId, slot.slotIndex):
return slot.collateral
return 0.u128
return 0'Tokens
proc emitSlotFilled*(market: MockMarket, requestId: RequestId, slotIndex: uint64) =
var subscriptions = market.subscriptions.onSlotFilled
@ -273,7 +279,7 @@ proc fillSlot*(
slotIndex: uint64,
proof: Groth16Proof,
host: Address,
collateral = 0.u128,
collateral = 0'Tokens,
) =
if error =? market.errorOnFillSlot:
raise error
@ -283,7 +289,7 @@ proc fillSlot*(
slotIndex: slotIndex,
proof: proof,
host: host,
timestamp: market.clock .? now,
timestamp: market.clock.now,
collateral: collateral,
)
market.filled.add(slot)
@ -295,7 +301,7 @@ method fillSlot*(
requestId: RequestId,
slotIndex: uint64,
proof: Groth16Proof,
collateral: UInt128,
collateral: Tokens,
) {.async: (raises: [CancelledError, MarketError]).} =
market.fillSlot(requestId, slotIndex, proof, market.signer, collateral)
@ -349,7 +355,7 @@ method submitProof*(
subscription.callback(id)
method markProofAsMissing*(
market: MockMarket, id: SlotId, period: Period
market: MockMarket, id: SlotId, period: ProofPeriod
) {.async: (raises: [CancelledError, MarketError]).} =
market.markedAsMissingProofs.add(id)
@ -360,7 +366,7 @@ proc setCanProofBeMarkedAsMissing*(mock: MockMarket, id: SlotId, required: bool)
mock.canBeMarkedAsMissing.excl(id)
method canProofBeMarkedAsMissing*(
market: MockMarket, id: SlotId, period: Period
market: MockMarket, id: SlotId, period: ProofPeriod
): Future[bool] {.async.} =
return market.canBeMarkedAsMissing.contains(id)
@ -495,7 +501,7 @@ method queryPastStorageRequestedEvents*(
): Future[seq[StorageRequested]] {.async.} =
return market.requested.map(
request =>
StorageRequested(requestId: request.id, ask: request.ask, expiry: request.expiry)
StorageRequested(requestId: request.id, ask: request.ask, expiry: market.requestExpiry[request.id])
)
method queryPastStorageRequestedEvents*(
@ -503,7 +509,7 @@ method queryPastStorageRequestedEvents*(
): Future[seq[StorageRequested]] {.async.} =
return market.requested.map(
request =>
StorageRequested(requestId: request.id, ask: request.ask, expiry: request.expiry)
StorageRequested(requestId: request.id, ask: request.ask, expiry: market.requestExpiry[request.id])
)
method queryPastSlotFilledEvents*(
@ -525,10 +531,7 @@ method queryPastSlotFilledEvents*(
): Future[seq[SlotFilled]] {.async.} =
let filtered = market.filled.filter(
proc(slot: MockSlot): bool =
if timestamp =? slot.timestamp:
return timestamp >= fromTime
else:
true
slot.timestamp >= fromTime
)
return filtered.map(
slot => SlotFilled(requestId: slot.requestId, slotIndex: slot.slotIndex)

View File

@ -28,8 +28,8 @@ method createReservation*(
slotSize: uint64,
requestId: RequestId,
slotIndex: uint64,
collateralPerByte: UInt256,
validUntil: SecondsSince1970,
collateralPerByte: Tokens,
validUntil: StorageTimestamp,
): Future[?!Reservation] {.async.} =
if self.createReservationThrowBytesOutOfBoundsError:
let error = newException(

View File

@ -5,10 +5,10 @@ type MockSlotQueueItem* = object
requestId*: RequestId
slotIndex*: uint16
slotSize*: uint64
duration*: uint64
pricePerBytePerSecond*: UInt256
collateral*: UInt256
expiry*: uint64
duration*: StorageDuration
pricePerBytePerSecond*: TokensPerSecond
collateral*: Tokens
expiry*: StorageTimestamp
seen*: bool
proc toSlotQueueItem*(item: MockSlotQueueItem): SlotQueueItem =
@ -17,8 +17,8 @@ proc toSlotQueueItem*(item: MockSlotQueueItem): SlotQueueItem =
slotIndex = item.slotIndex,
ask = StorageAsk(
slotSize: item.slotSize,
duration: item.duration.stuint(40),
pricePerBytePerSecond: item.pricePerBytePerSecond.stuint(96),
duration: item.duration,
pricePerBytePerSecond: item.pricePerBytePerSecond,
),
expiry = item.expiry,
seen = item.seen,

View File

@ -116,8 +116,7 @@ asyncchecksuite "Test Node - Host contracts":
let onStore = !sales.onStore
var request = StorageRequest.example
request.content.cid = verifiableBlock.cid
request.expiry =
(getTime() + DefaultBlockTtl.toTimesDuration + 1.hours).toUnix.stuint(40)
let expiry = (getTime() + DefaultBlockTtl.toTimesDuration + 1.hours).toUnix
var fetchedBytes: uint = 0
let onBlocks = proc(blocks: seq[bt.Block]): Future[?!void] {.async.} =
@ -125,7 +124,7 @@ asyncchecksuite "Test Node - Host contracts":
fetchedBytes += blk.data.len.uint
return success()
(await onStore(request, 1.uint64, onBlocks, isRepairing = false)).tryGet()
(await onStore(request, StorageTimestamp.init(expiry), 1.uint64, onBlocks, isRepairing = false)).tryGet()
check fetchedBytes == 12 * DefaultBlockSize.uint
let indexer = verifiable.protectedStrategy.init(
@ -139,4 +138,4 @@ asyncchecksuite "Test Node - Host contracts":
bytes = (await localStoreMetaDs.get(key)).tryGet
blkMd = BlockMetadata.decode(bytes).tryGet
check blkMd.expiry == request.expiry.toSecondsSince1970
check blkMd.expiry == expiry

View File

@ -188,11 +188,11 @@ asyncchecksuite "Test Node - Basic":
cid = manifestBlock.cid,
nodes = 5,
tolerance = 2,
duration = 100.stuint(40),
pricePerBytePerSecond = 1.stuint(96),
duration = 100'StorageDuration,
pricePerBytePerSecond = 1'TokensPerSecond,
proofProbability = 3.u256,
expiry = 200.stuint(40),
collateralPerByte = 1.u128,
expiry = 200'StorageDuration,
collateralPerByte = 1'Tokens,
)
).tryGet

View File

@ -3,6 +3,6 @@ import ../../helpers/mockclock
proc advanceToNextPeriod*(clock: MockClock, market: Market) =
let periodicity = market.periodicity()
let period = periodicity.periodOf(clock.now().Timestamp)
let period = periodicity.periodOf(clock.now())
let periodEnd = periodicity.periodEnd(period)
clock.set(periodEnd.toSecondsSince1970 + 1)

View File

@ -19,18 +19,18 @@ asyncchecksuite "sales state 'cancelled'":
let slotIndex = request.ask.slots div 2
let clock = MockClock.new()
let currentCollateral = UInt128.example
let currentCollateral = Tokens.example
var market: MockMarket
var state: SaleCancelled
var agent: SalesAgent
var reprocessSlotWas: ?bool
var returnedCollateralValue: ?UInt256
var returnedCollateralValue: ?Tokens
setup:
market = MockMarket.new()
let onCleanUp = proc(
reprocessSlot = false, returnedCollateral = UInt256.none
reprocessSlot = false, returnedCollateral = Tokens.none
) {.async.} =
reprocessSlotWas = some reprocessSlot
returnedCollateralValue = returnedCollateral
@ -40,10 +40,10 @@ asyncchecksuite "sales state 'cancelled'":
agent.onCleanUp = onCleanUp
state = SaleCancelled.new()
reprocessSlotWas = bool.none
returnedCollateralValue = UInt256.none
returnedCollateralValue = Tokens.none
teardown:
reprocessSlotWas = bool.none
returnedCollateralValue = UInt256.none
returnedCollateralValue = Tokens.none
test "calls onCleanUp with reprocessSlot = true, and returnedCollateral = currentCollateral":
market.fillSlot(
@ -91,7 +91,7 @@ asyncchecksuite "sales state 'cancelled'":
let next = await state.run(agent)
check next == none State
check eventually reprocessSlotWas == some false
check eventually returnedCollateralValue == UInt256.none
check eventually returnedCollateralValue == Tokens.none
test "calls onCleanUp and returns the collateral when an error is raised":
market.fillSlot(

View File

@ -24,7 +24,7 @@ asyncchecksuite "sales state 'errored'":
setup:
let onCleanUp = proc(
reprocessSlot = false, returnedCollateral = UInt256.none
reprocessSlot = false, returnedCollateral = Tokens.none
) {.async.} =
reprocessSlotWas = reprocessSlot

View File

@ -33,7 +33,7 @@ suite "sales state 'filled'":
proof: Groth16Proof.default,
)
market.requestEnds[request.id] = 321
market.requestEnds[request.id] = 321'StorageTimestamp
onExpiryUpdatePassedExpiry = -1
let onExpiryUpdate = proc(
rootCid: Cid, expiry: SecondsSince1970
@ -55,11 +55,11 @@ suite "sales state 'filled'":
slot.host = await market.getSigner()
market.filled = @[slot]
let expectedExpiry = 123
let expectedExpiry = 123'StorageTimestamp
market.requestEnds[request.id] = expectedExpiry
let next = await state.run(agent)
check !next of SaleProving
check onExpiryUpdatePassedExpiry == expectedExpiry
check onExpiryUpdatePassedExpiry == expectedExpiry.toSecondsSince1970
test "switches to error state when slot is filled by another host":
slot.host = Address.example

View File

@ -18,19 +18,19 @@ asyncchecksuite "sales state 'finished'":
let slotIndex = request.ask.slots div 2
let clock = MockClock.new()
let currentCollateral = UInt256.example
let currentCollateral = Tokens.example
var market: MockMarket
var state: SaleFinished
var agent: SalesAgent
var reprocessSlotWas = bool.none
var returnedCollateralValue = UInt256.none
var returnedCollateralValue = Tokens.none
var saleCleared = bool.none
setup:
market = MockMarket.new()
let onCleanUp = proc(
reprocessSlot = false, returnedCollateral = UInt256.none
reprocessSlot = false, returnedCollateral = Tokens.none
) {.async.} =
reprocessSlotWas = some reprocessSlot
returnedCollateralValue = returnedCollateral

View File

@ -24,7 +24,7 @@ asyncchecksuite "sales state 'ignored'":
setup:
let onCleanUp = proc(
reprocessSlot = false, returnedCollateral = UInt256.none
reprocessSlot = false, returnedCollateral = Tokens.none
) {.async.} =
reprocessSlotWas = reprocessSlot

View File

@ -18,7 +18,7 @@ asyncchecksuite "sales state 'payout'":
let slotIndex = request.ask.slots div 2
let clock = MockClock.new()
let currentCollateral = UInt128.example
let currentCollateral = Tokens.example
var market: MockMarket
var state: SalePayout
@ -41,4 +41,4 @@ asyncchecksuite "sales state 'payout'":
)
let next = await state.run(agent)
check !next of SaleFinished
check SaleFinished(!next).returnedCollateral == some collateral
check SaleFinished(!next).returnedCollateral == some currentCollateral

View File

@ -35,15 +35,15 @@ asyncchecksuite "sales state 'preparing'":
setup:
let collateral =
request.ask.collateralPerSlot.stuint(256) * request.ask.slots.u256
request.ask.collateralPerSlot * request.ask.slots
availability = Availability.init(
totalSize = request.ask.slotSize + 100.uint64,
freeSize = request.ask.slotSize + 100.uint64,
duration = request.ask.duration.u64 + 60,
minPricePerBytePerSecond = request.ask.pricePerBytePerSecond.stuint(256),
duration = request.ask.duration + 60'u8,
minPricePerBytePerSecond = request.ask.pricePerBytePerSecond,
totalCollateral = collateral,
enabled = true,
until = 0.SecondsSince1970,
until = 0'StorageTimestamp,
)
let repoDs = SQLiteDatastore.new(Memory).tryGet()
let metaDs = SQLiteDatastore.new(Memory).tryGet()
@ -57,7 +57,8 @@ asyncchecksuite "sales state 'preparing'":
context.reservations = reservations
agent = newSalesAgent(context, request.id, slotIndex, request.some)
market.requestEnds[request.id] = clock.now() + cast[int64](request.ask.duration)
market.requestEnds[request.id] =
StorageTimestamp.init(clock.now()) + request.ask.duration
teardown:
await repo.stop()
@ -81,7 +82,7 @@ asyncchecksuite "sales state 'preparing'":
availability.minPricePerBytePerSecond,
availability.totalCollateral,
enabled,
until = 0.SecondsSince1970,
until = 0'StorageTimestamp,
)
availability = a.get

View File

@ -23,7 +23,7 @@ asyncchecksuite "Reservations module":
repoDs: Datastore
metaDs: Datastore
reservations: Reservations
collateralPerByte: UInt256
collateralPerByte: Tokens
let
repoTmp = TempLevelDb.new()
metaTmp = TempLevelDb.new()
@ -34,16 +34,16 @@ asyncchecksuite "Reservations module":
metaDs = metaTmp.newDb()
repo = RepoStore.new(repoDs, metaDs)
reservations = Reservations.new(repo)
collateralPerByte = uint8.example.u256
collateralPerByte = Tokens.init(uint8.example)
teardown:
await repoTmp.destroyDb()
await metaTmp.destroyDb()
proc createAvailability(enabled = true, until = 0.SecondsSince1970): Availability =
proc createAvailability(enabled = true, until = 0'StorageTimestamp): Availability =
let example = Availability.example(collateralPerByte)
let totalSize = rand(100000 .. 200000).uint64
let totalCollateral = totalSize.u256 * collateralPerByte
let totalCollateral = collateralPerByte * totalSize
let availability = waitFor reservations.createAvailability(
totalSize, example.duration, example.minPricePerBytePerSecond, totalCollateral,
enabled, until,
@ -52,9 +52,9 @@ asyncchecksuite "Reservations module":
proc createReservation(availability: Availability): Reservation =
let size = rand(1 ..< availability.freeSize.int)
let validUntil = getTime().toUnix() + 30.SecondsSince1970
let validUntil = StorageTimestamp.init(getTime().toUnix() + 30)
let reservation = waitFor reservations.createReservation(
availability.id, size.uint64, RequestId.example, uint64.example, 1.u256,
availability.id, size.uint64, RequestId.example, uint64.example, 1'Tokens,
validUntil,
)
return reservation.get
@ -69,10 +69,22 @@ asyncchecksuite "Reservations module":
test "generates unique ids for storage availability":
let availability1 = Availability.init(
1.uint64, 2.uint64, 3.uint64, 4.u256, 5.u256, true, 0.SecondsSince1970
1.uint64,
2.uint64,
3'StorageDuration,
4'TokensPerSecond,
5'Tokens,
true,
0'StorageTimestamp
)
let availability2 = Availability.init(
1.uint64, 2.uint64, 3.uint64, 4.u256, 5.u256, true, 0.SecondsSince1970
1.uint64,
2.uint64,
3'StorageDuration,
4'TokensPerSecond,
5'Tokens,
true,
0'StorageTimestamp
)
check availability1.id != availability2.id
@ -136,9 +148,9 @@ asyncchecksuite "Reservations module":
test "cannot create reservation with non-existant availability":
let availability = Availability.example
let validUntil = getTime().toUnix() + 30.SecondsSince1970
let validUntil = StorageTimestamp.init(getTime().toUnix() + 30)
let created = await reservations.createReservation(
availability.id, uint64.example, RequestId.example, uint64.example, 1.u256,
availability.id, uint64.example, RequestId.example, uint64.example, 1'Tokens,
validUntil,
)
check created.isErr
@ -146,13 +158,13 @@ asyncchecksuite "Reservations module":
test "cannot create reservation larger than availability size":
let availability = createAvailability()
let validUntil = getTime().toUnix() + 30.SecondsSince1970
let validUntil = StorageTimestamp.init(getTime().toUnix() + 30)
let created = await reservations.createReservation(
availability.id,
availability.totalSize + 1,
RequestId.example,
uint64.example,
UInt256.example,
Tokens.example,
validUntil,
)
check created.isErr
@ -161,19 +173,19 @@ asyncchecksuite "Reservations module":
test "cannot create reservation larger than availability size - concurrency test":
proc concurrencyTest(): Future[void] {.async.} =
let availability = createAvailability()
let validUntil = getTime().toUnix() + 30.SecondsSince1970
let validUntil = StorageTimestamp.init(getTime().toUnix() + 30)
let one = reservations.createReservation(
availability.id,
availability.totalSize - 1,
RequestId.example,
uint64.example,
UInt256.example,
Tokens.example,
validUntil,
)
let two = reservations.createReservation(
availability.id, availability.totalSize, RequestId.example, uint64.example,
UInt256.example, validUntil,
Tokens.example, validUntil
)
let oneResult = await one
@ -280,42 +292,15 @@ asyncchecksuite "Reservations module":
test "create availability set until to 0 by default":
let availability = createAvailability()
check availability.until == 0.SecondsSince1970
check availability.until == 0'StorageTimestamp
test "create availability whith correct values":
var until = getTime().toUnix()
var until = StorageTimestamp.init(getTime().toUnix())
let availability = createAvailability(enabled = false, until = until)
check availability.enabled == false
check availability.until == until
test "create an availability fails when trying set until with a negative value":
let totalSize = rand(100000 .. 200000).uint64
let example = Availability.example(collateralPerByte)
let totalCollateral = totalSize.u256 * collateralPerByte
let result = await reservations.createAvailability(
totalSize,
example.duration,
example.minPricePerBytePerSecond,
totalCollateral,
enabled = true,
until = -1.SecondsSince1970,
)
check result.isErr
check result.error of UntilOutOfBoundsError
test "update an availability fails when trying set until with a negative value":
let until = getTime().toUnix()
let availability = createAvailability(until = until)
availability.until = -1
let result = await reservations.update(availability)
check result.isErr
check result.error of UntilOutOfBoundsError
test "reservation can be partially released":
let availability = createAvailability()
let reservation = createReservation(availability)
@ -392,7 +377,7 @@ asyncchecksuite "Reservations module":
var added: Availability
reservations.OnAvailabilitySaved = proc(a: Availability) {.async: (raises: []).} =
added = a
availability.duration += 1
availability.duration += 1'StorageDuration
discard await reservations.update(availability)
check added == availability
@ -402,7 +387,7 @@ asyncchecksuite "Reservations module":
var called = false
reservations.OnAvailabilitySaved = proc(a: Availability) {.async: (raises: []).} =
called = true
availability.duration -= 1
availability.duration -= 1'StorageDuration
discard await reservations.update(availability)
check not called
@ -412,7 +397,7 @@ asyncchecksuite "Reservations module":
var added: Availability
reservations.OnAvailabilitySaved = proc(a: Availability) {.async: (raises: []).} =
added = a
availability.minPricePerBytePerSecond += 1.u256
availability.minPricePerBytePerSecond += 1'TokensPerSecond
discard await reservations.update(availability)
check added == availability
@ -422,7 +407,7 @@ asyncchecksuite "Reservations module":
var called = false
reservations.OnAvailabilitySaved = proc(a: Availability) {.async: (raises: []).} =
called = true
availability.minPricePerBytePerSecond -= 1.u256
availability.minPricePerBytePerSecond -= 1'TokensPerSecond
discard await reservations.update(availability)
check not called
@ -432,7 +417,7 @@ asyncchecksuite "Reservations module":
var added: Availability
reservations.OnAvailabilitySaved = proc(a: Availability) {.async: (raises: []).} =
added = a
availability.totalCollateral = availability.totalCollateral + 1.u256
availability.totalCollateral = availability.totalCollateral + 1'Tokens
discard await reservations.update(availability)
check added == availability
@ -442,14 +427,14 @@ asyncchecksuite "Reservations module":
var called = false
reservations.OnAvailabilitySaved = proc(a: Availability) {.async: (raises: []).} =
called = true
availability.totalCollateral = availability.totalCollateral - 1.u256
availability.totalCollateral = availability.totalCollateral - 1'Tokens
discard await reservations.update(availability)
check not called
test "availabilities can be found":
let availability = createAvailability()
let validUntil = getTime().toUnix() + 30.SecondsSince1970
let validUntil = StorageTimestamp.init(getTime().toUnix() + 30)
let found = await reservations.findAvailability(
availability.freeSize, availability.duration,
availability.minPricePerBytePerSecond, collateralPerByte, validUntil,
@ -460,7 +445,7 @@ asyncchecksuite "Reservations module":
test "does not find an availability when is it disabled":
let availability = createAvailability(enabled = false)
let validUntil = getTime().toUnix() + 30.SecondsSince1970
let validUntil = StorageTimestamp.init(getTime().toUnix() + 30)
let found = await reservations.findAvailability(
availability.freeSize, availability.duration,
availability.minPricePerBytePerSecond, collateralPerByte, validUntil,
@ -470,9 +455,9 @@ asyncchecksuite "Reservations module":
test "finds an availability when the until date is after the duration":
let example = Availability.example(collateralPerByte)
let until = getTime().toUnix() + example.duration.SecondsSince1970
let until = StorageTimestamp.init(getTime().toUnix()) + example.duration
let availability = createAvailability(until = until)
let validUntil = getTime().toUnix() + 30.SecondsSince1970
let validUntil = StorageTimestamp.init(getTime().toUnix() + 30)
let found = await reservations.findAvailability(
availability.freeSize, availability.duration,
availability.minPricePerBytePerSecond, collateralPerByte, validUntil,
@ -483,9 +468,9 @@ asyncchecksuite "Reservations module":
test "does not find an availability when the until date is before the duration":
let example = Availability.example(collateralPerByte)
let until = getTime().toUnix() + 1.SecondsSince1970
let until = StorageTimestamp.init(getTime().toUnix() + 1)
let availability = createAvailability(until = until)
let validUntil = getTime().toUnix() + 30.SecondsSince1970
let validUntil = StorageTimestamp.init(getTime().toUnix() + 30)
let found = await reservations.findAvailability(
availability.freeSize, availability.duration,
availability.minPricePerBytePerSecond, collateralPerByte, validUntil,
@ -495,7 +480,7 @@ asyncchecksuite "Reservations module":
test "non-matching availabilities are not found":
let availability = createAvailability()
let validUntil = getTime().toUnix() + 30.SecondsSince1970
let validUntil = StorageTimestamp.init(getTime().toUnix() + 30)
let found = await reservations.findAvailability(
availability.freeSize + 1,
availability.duration,
@ -508,7 +493,7 @@ asyncchecksuite "Reservations module":
test "non-existent availability cannot be found":
let availability = Availability.example
let validUntil = getTime().toUnix() + 30.SecondsSince1970
let validUntil = StorageTimestamp.init(getTime().toUnix() + 30)
let found = await reservations.findAvailability(
availability.freeSize, availability.duration,
availability.minPricePerBytePerSecond, collateralPerByte, validUntil,
@ -533,11 +518,11 @@ asyncchecksuite "Reservations module":
test "fails to create availability with size that is larger than available quota":
let created = await reservations.createAvailability(
DefaultQuotaBytes.uint64 + 1,
uint64.example,
UInt256.example,
UInt256.example,
StorageDuration.example,
TokensPerSecond.example,
Tokens.example,
enabled = true,
until = 0.SecondsSince1970,
until = 0'StorageTimestamp,
)
check created.isErr
check created.error of ReserveFailedError

View File

@ -37,21 +37,20 @@ asyncchecksuite "Sales - start":
var repo: RepoStore
var queue: SlotQueue
var itemsProcessed: seq[SlotQueueItem]
var expiry: SecondsSince1970
setup:
request = StorageRequest(
ask: StorageAsk(
slots: 4,
slotSize: 100.uint64,
duration: 60.stuint(40),
pricePerBytePerSecond: 1.stuint(96),
collateralPerByte: 1.u128,
duration: 60'StorageDuration,
pricePerBytePerSecond: 1'TokensPerSecond,
collateralPerByte: 1'Tokens,
),
content: StorageContent(
cid: Cid.init("zb2rhheVmk3bLks5MgzTqyznLu1zqGH5jrfTA1eAZXrjx7Vob").tryGet
),
expiry: (getTime() + initDuration(hours = 1)).toUnix.stuint(40),
expiry: 60'StorageDuration,
)
market = MockMarket.new()
@ -63,7 +62,11 @@ asyncchecksuite "Sales - start":
sales = Sales.new(market, clock, repo)
reservations = sales.context.reservations
sales.onStore = proc(
request: StorageRequest, slot: uint64, onBatch: BatchProc, isRepairing = false
request: StorageRequest,
expiry: StorageTimestamp,
slot: uint64,
onBatch: BatchProc,
isRepairing = false
): Future[?!void] {.async.} =
return success()
@ -78,8 +81,6 @@ asyncchecksuite "Sales - start":
): Future[?!Groth16Proof] {.async.} =
return success(proof)
itemsProcessed = @[]
expiry = (clock.now() + 42)
request.expiry = expiry.stuint(40)
teardown:
await sales.stop()
@ -100,7 +101,6 @@ asyncchecksuite "Sales - start":
request.ask.slots = 2
market.requested = @[request]
market.requestState[request.id] = RequestState.New
market.requestExpiry[request.id] = expiry
let slot0 = MockSlot(requestId: request.id, slotIndex: 0, proof: proof, host: me)
await fillSlot(slot0.slotIndex)
@ -129,9 +129,9 @@ asyncchecksuite "Sales":
metaTmp = TempLevelDb.new()
var totalAvailabilitySize: uint64
var minPricePerBytePerSecond: UInt256
var requestedCollateralPerByte: UInt128
var totalCollateral: UInt256
var minPricePerBytePerSecond: TokensPerSecond
var requestedCollateralPerByte: Tokens
var totalCollateral: Tokens
var availability: Availability
var request: StorageRequest
var sales: Sales
@ -144,37 +144,36 @@ asyncchecksuite "Sales":
setup:
totalAvailabilitySize = 100.uint64
minPricePerBytePerSecond = 1.u256
requestedCollateralPerByte = 1.u128
totalCollateral = requestedCollateralPerByte.stuint(256) * totalAvailabilitySize.stuint(256)
minPricePerBytePerSecond = 1'TokensPerSecond
requestedCollateralPerByte = 1'Tokens
totalCollateral = requestedCollateralPerByte * totalAvailabilitySize
availability = Availability.init(
totalSize = totalAvailabilitySize,
freeSize = totalAvailabilitySize,
duration = 60.uint64,
duration = 60'StorageDuration,
minPricePerBytePerSecond = minPricePerBytePerSecond,
totalCollateral = totalCollateral,
enabled = true,
until = 0.SecondsSince1970,
until = 0'StorageTimestamp,
)
request = StorageRequest(
ask: StorageAsk(
slots: 4,
slotSize: 100.uint64,
duration: 60.stuint(40),
pricePerBytePerSecond: minPricePerBytePerSecond.stuint(96),
collateralPerByte: 1.u128,
duration: 60'StorageDuration,
pricePerBytePerSecond: minPricePerBytePerSecond,
collateralPerByte: 1'Tokens,
),
content: StorageContent(
cid: Cid.init("zb2rhheVmk3bLks5MgzTqyznLu1zqGH5jrfTA1eAZXrjx7Vob").tryGet
),
expiry: (getTime() + initDuration(hours = 1)).toUnix.stuint(40),
expiry: 60'StorageDuration,
)
market = MockMarket.new()
let me = await market.getSigner()
market.activeSlots[me] = @[]
market.requestEnds[request.id] = request.expiry.toSecondsSince1970
clock = MockClock.new()
let repoDs = repoTmp.newDb()
@ -184,7 +183,11 @@ asyncchecksuite "Sales":
sales = Sales.new(market, clock, repo)
reservations = sales.context.reservations
sales.onStore = proc(
request: StorageRequest, slot: uint64, onBatch: BatchProc, isRepairing = false
request: StorageRequest,
expiry: StorageTimestamp,
slot: uint64,
onBatch: BatchProc,
isRepairing = false
): Future[?!void] {.async.} =
return success()
@ -224,7 +227,7 @@ asyncchecksuite "Sales":
let key = availability.id.key.get
(waitFor reservations.get(key, Availability)).get
proc createAvailability(enabled = true, until = 0.SecondsSince1970) =
proc createAvailability(enabled = true, until = 0'StorageTimestamp) =
let a = waitFor reservations.createAvailability(
availability.totalSize, availability.duration,
availability.minPricePerBytePerSecond, availability.totalCollateral, enabled,
@ -233,7 +236,7 @@ asyncchecksuite "Sales":
availability = a.get # update id
proc notProcessed(itemsProcessed: seq[SlotQueueItem], request: StorageRequest): bool =
let collateral =request.ask.collateralPerSlot.stuint(256)
let collateral = request.ask.collateralPerSlot
let items = SlotQueueItem.init(request, collateral)
for i in 0 ..< items.len:
if itemsProcessed.contains(items[i]):
@ -254,7 +257,7 @@ asyncchecksuite "Sales":
done.complete()
var request1 = StorageRequest.example
request1.ask.collateralPerByte = request.ask.collateralPerByte + 1
request1.ask.collateralPerByte = request.ask.collateralPerByte + 1'u8
createAvailability()
# saturate queue
while queue.len < queue.size - 1:
@ -281,7 +284,7 @@ asyncchecksuite "Sales":
done.complete()
createAvailability()
await market.requestStorage(request)
let collateral = request.ask.collateralPerSlot.stuint(256)
let collateral = request.ask.collateralPerSlot
let items = SlotQueueItem.init(request, collateral)
check eventually items.allIt(itemsProcessed.contains(it))
@ -298,14 +301,14 @@ asyncchecksuite "Sales":
test "removes slot index from slot queue once SlotFilled emitted":
let request1 = await addRequestToSaturatedQueue()
market.emitSlotFilled(request1.id, 1.uint64)
let collateral = request1.ask.collateralPerSlot.stuint(256)
let collateral = request1.ask.collateralPerSlot
let expected = SlotQueueItem.init(request1, 1'u16, collateral)
check always (not itemsProcessed.contains(expected))
test "removes slot index from slot queue once SlotReservationsFull emitted":
let request1 = await addRequestToSaturatedQueue()
market.emitSlotReservationsFull(request1.id, 1.uint64)
let collateral = request1.ask.collateralPerSlot.stuint(256)
let collateral = request1.ask.collateralPerSlot
let expected = SlotQueueItem.init(request1, 1'u16, collateral)
check always (not itemsProcessed.contains(expected))
@ -322,14 +325,14 @@ asyncchecksuite "Sales":
market.emitSlotFreed(request.id, 2.uint64)
let collateral = request.ask.collateralPerSlot.stuint(256)
let collateral = request.ask.collateralPerSlot
let expected = SlotQueueItem.init(request, 2.uint16, collateral)
check eventually itemsProcessed.contains(expected)
test "items in queue are readded (and marked seen) once ignored":
await market.requestStorage(request)
let collateral = request.ask.collateralPerSlot.stuint(256)
let collateral = request.ask.collateralPerSlot
let items = SlotQueueItem.init(request, collateral)
check eventually queue.len > 0
# queue starts paused, allow items to be added to the queue
@ -351,7 +354,7 @@ asyncchecksuite "Sales":
test "queue is paused once availability is insufficient to service slots in queue":
createAvailability() # enough to fill a single slot
await market.requestStorage(request)
let collateral = request.ask.collateralPerSlot.stuint(256)
let collateral = request.ask.collateralPerSlot
let items = SlotQueueItem.init(request, collateral)
check eventually queue.len > 0
# queue starts paused, allow items to be added to the queue
@ -369,7 +372,7 @@ asyncchecksuite "Sales":
test "availability size is reduced by request slot size when fully downloaded":
sales.onStore = proc(
request: StorageRequest, slot: uint64, onBatch: BatchProc, isRepairing = false
request: StorageRequest, expiry: StorageTimestamp, slot: uint64, onBatch: BatchProc, isRepairing = false
): Future[?!void] {.async.} =
let blk = bt.Block.new(@[1.byte]).get
await onBatch(blk.repeat(request.ask.slotSize.int))
@ -382,7 +385,7 @@ asyncchecksuite "Sales":
test "bytes are returned to availability once finished":
var slotIndex = 0.uint64
sales.onStore = proc(
request: StorageRequest, slot: uint64, onBatch: BatchProc, isRepairing = false
request: StorageRequest, expiry: StorageTimestamp, slot: uint64, onBatch: BatchProc, isRepairing = false
): Future[?!void] {.async.} =
slotIndex = slot
let blk = bt.Block.new(@[1.byte]).get
@ -400,12 +403,12 @@ asyncchecksuite "Sales":
# complete request
market.slotState[request.slotId(slotIndex)] = SlotState.Finished
clock.advance(request.ask.duration.truncate(int64))
clock.advance(request.ask.duration.u64.int64)
check eventually getAvailability().freeSize == origSize
test "ignores download when duration not long enough":
availability.duration = request.ask.duration.truncate(uint64) - 1
availability.duration = request.ask.duration - 1'u8
createAvailability()
await market.requestStorage(request)
check wasIgnored()
@ -417,8 +420,8 @@ asyncchecksuite "Sales":
check wasIgnored()
test "ignores request when reward is too low":
let price = request.ask.pricePerBytePerSecond.stuint(256)
availability.minPricePerBytePerSecond = price + 1
let price = request.ask.pricePerBytePerSecond
availability.minPricePerBytePerSecond = price + 1'u8
createAvailability()
await market.requestStorage(request)
check wasIgnored()
@ -445,20 +448,21 @@ asyncchecksuite "Sales":
check wasIgnored()
test "ignores request when availability until terminates before the duration":
let until = getTime().toUnix()
let until = StorageTimestamp.init(getTime().toUnix())
createAvailability(until = until)
await market.requestStorage(request)
check wasIgnored()
test "retrieves request when availability until terminates after the duration":
let requestEnd = getTime().toUnix() + cast[int64](request.ask.duration)
let until = requestEnd + 1
let requestEnd =
StorageTimestamp.init(getTime().toUnix()) + request.ask.duration
let until = requestEnd + 1'StorageDuration
createAvailability(until = until)
var storingRequest: StorageRequest
sales.onStore = proc(
request: StorageRequest, slot: uint64, onBatch: BatchProc, isRepairing = false
request: StorageRequest, expiry: StorageTimestamp, slot: uint64, onBatch: BatchProc, isRepairing = false
): Future[?!void] {.async.} =
storingRequest = request
return success()
@ -471,7 +475,7 @@ asyncchecksuite "Sales":
var storingRequest: StorageRequest
var storingSlot: uint64
sales.onStore = proc(
request: StorageRequest, slot: uint64, onBatch: BatchProc, isRepairing = false
request: StorageRequest, expiry: StorageTimestamp, slot: uint64, onBatch: BatchProc, isRepairing = false
): Future[?!void] {.async.} =
storingRequest = request
storingSlot = slot
@ -484,7 +488,7 @@ asyncchecksuite "Sales":
test "makes storage available again when data retrieval fails":
let error = newException(IOError, "data retrieval failed")
sales.onStore = proc(
request: StorageRequest, slot: uint64, onBatch: BatchProc, isRepairing = false
request: StorageRequest, expiry: StorageTimestamp, slot: uint64, onBatch: BatchProc, isRepairing = false
): Future[?!void] {.async.} =
return failure(error)
createAvailability()
@ -553,7 +557,7 @@ asyncchecksuite "Sales":
test "makes storage available again when other host fills the slot":
let otherHost = Address.example
sales.onStore = proc(
request: StorageRequest, slot: uint64, onBatch: BatchProc, isRepairing = false
request: StorageRequest, expiry: StorageTimestamp, slot: uint64, onBatch: BatchProc, isRepairing = false
): Future[?!void] {.async.} =
await sleepAsync(chronos.hours(1))
return success()
@ -564,12 +568,9 @@ asyncchecksuite "Sales":
check eventually (await reservations.all(Availability)).get == @[availability]
test "makes storage available again when request expires":
let expiry = getTime().toUnix() + 10
market.requestExpiry[request.id] = expiry
let origSize = availability.freeSize
sales.onStore = proc(
request: StorageRequest, slot: uint64, onBatch: BatchProc, isRepairing = false
request: StorageRequest, expiry: StorageTimestamp, slot: uint64, onBatch: BatchProc, isRepairing = false
): Future[?!void] {.async.} =
await sleepAsync(chronos.hours(1))
return success()
@ -580,23 +581,21 @@ asyncchecksuite "Sales":
# would otherwise not set the timeout early enough as it uses `clock.now` in the deadline calculation.
await sleepAsync(chronos.milliseconds(100))
market.requestState[request.id] = RequestState.Cancelled
clock.set(expiry + 1)
clock.set(market.requestExpiry[request.id].toSecondsSince1970 + 1)
check eventually (await reservations.all(Availability)).get == @[availability]
check getAvailability().freeSize == origSize
test "verifies that request is indeed expired from onchain before firing onCancelled":
let expiry = getTime().toUnix() + 10
# ensure only one slot, otherwise once bytes are returned to the
# availability, the queue will be unpaused and availability will be consumed
# by other slots
request.ask.slots = 1
market.requestExpiry[request.id] = expiry
market.requestEnds[request.id] =
getTime().toUnix() + cast[int64](request.ask.duration)
StorageTimestamp.init(getTime().toUnix()) + request.ask.duration
let origSize = availability.freeSize
sales.onStore = proc(
request: StorageRequest, slot: uint64, onBatch: BatchProc, isRepairing = false
request: StorageRequest, expiry: StorageTimestamp, slot: uint64, onBatch: BatchProc, isRepairing = false
): Future[?!void] {.async.} =
await sleepAsync(chronos.hours(1))
return success()
@ -608,7 +607,7 @@ asyncchecksuite "Sales":
# If we would not await, then the `clock.set` would run "too fast" as the `subscribeCancellation()`
# would otherwise not set the timeout early enough as it uses `clock.now` in the deadline calculation.
await sleepAsync(chronos.milliseconds(100))
clock.set(expiry + 1)
clock.set(market.requestExpiry[request.id].toSecondsSince1970 + 1)
check getAvailability().freeSize == 0
market.requestState[request.id] = RequestState.Cancelled
@ -621,7 +620,6 @@ asyncchecksuite "Sales":
request.ask.slots = 2
market.requested = @[request]
market.requestState[request.id] = RequestState.New
market.requestEnds[request.id] = request.expiry.toSecondsSince1970
proc fillSlot(slotIdx: uint64 = 0) {.async.} =
let address = await market.getSigner()
@ -651,9 +649,9 @@ asyncchecksuite "Sales":
test "deletes inactive reservations on load":
createAvailability()
let validUntil = getTime().toUnix() + 30.SecondsSince1970
let validUntil = StorageTimestamp.init(getTime().toUnix() + 30)
discard await reservations.createReservation(
availability.id, 100.uint64, RequestId.example, 0.uint64, UInt256.example,
availability.id, 100.uint64, RequestId.example, 0.uint64, Tokens.example,
validUntil,
)
check (await reservations.all(Reservation)).get.len == 1
@ -662,16 +660,16 @@ asyncchecksuite "Sales":
check getAvailability().freeSize == availability.freeSize # was restored
test "update an availability fails when trying change the until date before an existing reservation":
let until = getTime().toUnix() + 300.SecondsSince1970
let until = StorageTimestamp.init(getTime().toUnix() + 300)
createAvailability(until = until)
market.requestEnds[request.id] =
getTime().toUnix() + cast[int64](request.ask.duration)
StorageTimestamp.init(getTime().toUnix()) + request.ask.duration
await market.requestStorage(request)
await allowRequestToStart()
availability.until = getTime().toUnix()
availability.until = StorageTimestamp.init(getTime().toUnix())
let result = await reservations.update(availability)
check result.isErr

View File

@ -33,6 +33,7 @@ method onSlotFilled*(
asyncchecksuite "Sales agent":
let request = StorageRequest.example
var expiry: StorageTimestamp
var agent: SalesAgent
var context: SalesContext
var slotIndex: uint64
@ -41,7 +42,7 @@ asyncchecksuite "Sales agent":
setup:
market = MockMarket.new()
let expiry = getTime().toUnix() + request.expiry.toSecondsSince1970
expiry = StorageTimestamp.init(getTime().toUnix()) + request.expiry
market.requestExpiry[request.id] = expiry
clock = MockClock.new()
context = SalesContext(market: market, clock: clock)
@ -84,7 +85,7 @@ asyncchecksuite "Sales agent":
agent.start(MockState.new())
await agent.subscribe()
market.requestState[request.id] = RequestState.Cancelled
clock.set(market.requestExpiry[request.id] + 1)
clock.set(expiry.toSecondsSince1970 + 1)
check eventually onCancelCalled
for requestState in {
@ -94,7 +95,7 @@ asyncchecksuite "Sales agent":
agent.start(MockState.new())
await agent.subscribe()
market.requestState[request.id] = requestState
clock.set(market.requestExpiry[request.id] + 1)
clock.set(expiry.toSecondsSince1970 + 1)
await sleepAsync(100.millis)
check not onCancelCalled
@ -103,7 +104,7 @@ asyncchecksuite "Sales agent":
agent.start(MockState.new())
await agent.subscribe()
market.requestState[request.id] = requestState
clock.set(market.requestExpiry[request.id] + 1)
clock.set(expiry.toSecondsSince1970 + 1)
check eventually agent.data.cancelled.finished
test "cancelled future is finished (cancelled) when onFulfilled called":

View File

@ -65,7 +65,6 @@ suite "Slot queue workers":
doneProcessing.complete()
setup:
let request = StorageRequest.example
queue = SlotQueue.new(maxSize = 5, maxWorkers = 3)
queue.onProcessSlot = onProcessSlot
@ -166,23 +165,21 @@ suite "Slot queue":
test "correctly compares SlotQueueItems":
var requestA = StorageRequest.example
requestA.ask.duration = 1.stuint(40)
requestA.ask.pricePerBytePerSecond = 1.stuint(96)
check requestA.ask.pricePerSlot == 1.u128 * requestA.ask.slotSize.u128
requestA.ask.collateralPerByte = 100000.u128
requestA.expiry = 1001.stuint(40)
requestA.ask.duration = 1'StorageDuration
requestA.ask.pricePerBytePerSecond = 1'TokensPerSecond
check requestA.ask.pricePerSlot == 1'Tokens * requestA.ask.slotSize
requestA.ask.collateralPerByte = 100000'Tokens
var requestB = StorageRequest.example
requestB.ask.duration = 100.stuint(40)
requestB.ask.pricePerBytePerSecond = 1000.stuint(96)
check requestB.ask.pricePerSlot == 100000.u128 * requestB.ask.slotSize.u128
requestB.ask.collateralPerByte = 1.u128
requestB.expiry = 1000.stuint(40)
requestB.ask.duration = 100'StorageDuration
requestB.ask.pricePerBytePerSecond = 1000'TokensPerSecond
check requestB.ask.pricePerSlot == 100000'Tokens * requestB.ask.slotSize
requestB.ask.collateralPerByte = 1'Tokens
let itemA =
SlotQueueItem.init(requestA, 0, requestA.ask.collateralPerSlot.stuint(256))
SlotQueueItem.init(requestA, 0, requestA.ask.collateralPerSlot)
let itemB =
SlotQueueItem.init(requestB, 0, requestB.ask.collateralPerSlot.stuint(256))
SlotQueueItem.init(requestB, 0, requestB.ask.collateralPerSlot)
check itemB < itemA # B higher priority than A
check itemA > itemB
@ -192,20 +189,20 @@ suite "Slot queue":
requestId: request.id,
slotIndex: 0,
slotSize: 1.uint64,
duration: 1.uint64,
pricePerBytePerSecond: 2.u256, # profitability is higher (good)
collateral: 1.u256,
expiry: 1.uint64,
duration: 1'StorageDuration,
pricePerBytePerSecond: 2'TokensPerSecond, # profitability is higher (good)
collateral: 1'Tokens,
expiry: 1'StorageTimestamp,
seen: true, # seen (bad), more weight than profitability
)
let itemB = MockSlotQueueItem(
requestId: request.id,
slotIndex: 0,
slotSize: 1.uint64,
duration: 1.uint64,
pricePerBytePerSecond: 1.u256, # profitability is lower (bad)
collateral: 1.u256,
expiry: 1.uint64,
duration: 1'StorageDuration,
pricePerBytePerSecond: 1'TokensPerSecond, # profitability is lower (bad)
collateral: 1'Tokens,
expiry: 1'StorageTimestamp,
seen: false, # not seen (good)
)
check itemB.toSlotQueueItem < itemA.toSlotQueueItem # B higher priority than A
@ -217,21 +214,21 @@ suite "Slot queue":
requestId: request.id,
slotIndex: 0,
slotSize: 1.uint64,
duration: 1.uint64,
pricePerBytePerSecond: 1.u256, # reward is lower (bad)
collateral: 1.u256, # collateral is lower (good)
expiry: 1.uint64,
duration: 1'StorageDuration,
pricePerBytePerSecond: 1'TokensPerSecond, # reward is lower (bad)
collateral: 1'Tokens, # collateral is lower (good)
expiry: 1'StorageTimestamp,
seen: false,
)
let itemB = MockSlotQueueItem(
requestId: request.id,
slotIndex: 0,
slotSize: 1.uint64,
duration: 1.uint64,
pricePerBytePerSecond: 2.u256,
duration: 1'StorageDuration,
pricePerBytePerSecond: 2'TokensPerSecond,
# reward is higher (good), more weight than collateral
collateral: 2.u256, # collateral is higher (bad)
expiry: 1.uint64,
collateral: 2'Tokens, # collateral is higher (bad)
expiry: 1'StorageTimestamp,
seen: false,
)
@ -243,20 +240,20 @@ suite "Slot queue":
requestId: request.id,
slotIndex: 0,
slotSize: 1.uint64,
duration: 1.uint64,
pricePerBytePerSecond: 1.u256,
collateral: 2.u256, # collateral is higher (bad)
expiry: 2.uint64, # expiry is longer (good)
duration: 1'StorageDuration,
pricePerBytePerSecond: 1'TokensPerSecond,
collateral: 2'Tokens, # collateral is higher (bad)
expiry: 2'StorageTimestamp, # expiry is longer (good)
seen: false,
)
let itemB = MockSlotQueueItem(
requestId: request.id,
slotIndex: 0,
slotSize: 1.uint64,
duration: 1.uint64,
pricePerBytePerSecond: 1.u256,
collateral: 1.u256, # collateral is lower (good), more weight than expiry
expiry: 1.uint64, # expiry is shorter (bad)
duration: 1'StorageDuration,
pricePerBytePerSecond: 1'TokensPerSecond,
collateral: 1'Tokens, # collateral is lower (good), more weight than expiry
expiry: 1'StorageTimestamp, # expiry is shorter (bad)
seen: false,
)
@ -268,20 +265,20 @@ suite "Slot queue":
requestId: request.id,
slotIndex: 0,
slotSize: 1.uint64, # slotSize is smaller (good)
duration: 1.uint64,
pricePerBytePerSecond: 1.u256,
collateral: 1.u256,
expiry: 1.uint64, # expiry is shorter (bad)
duration: 1'StorageDuration,
pricePerBytePerSecond: 1'TokensPerSecond,
collateral: 1'Tokens,
expiry: 1'StorageTimestamp, # expiry is shorter (bad)
seen: false,
)
let itemB = MockSlotQueueItem(
requestId: request.id,
slotIndex: 0,
slotSize: 2.uint64, # slotSize is larger (bad)
duration: 1.uint64,
pricePerBytePerSecond: 1.u256,
collateral: 1.u256,
expiry: 2.uint64, # expiry is longer (good), more weight than slotSize
duration: 1'StorageDuration,
pricePerBytePerSecond: 1'TokensPerSecond,
collateral: 1'Tokens,
expiry: 2'StorageTimestamp, # expiry is longer (good), more weight than slotSize
seen: false,
)
@ -293,20 +290,20 @@ suite "Slot queue":
requestId: request.id,
slotIndex: 0,
slotSize: 2.uint64, # slotSize is larger (bad)
duration: 1.uint64,
pricePerBytePerSecond: 1.u256,
collateral: 1.u256,
expiry: 1.uint64, # expiry is shorter (bad)
duration: 1'StorageDuration,
pricePerBytePerSecond: 1'TokensPerSecond,
collateral: 1'Tokens,
expiry: 1'StorageTimestamp, # expiry is shorter (bad)
seen: false,
)
let itemB = MockSlotQueueItem(
requestId: request.id,
slotIndex: 0,
slotSize: 1.uint64, # slotSize is smaller (good)
duration: 1.uint64,
pricePerBytePerSecond: 1.u256,
collateral: 1.u256,
expiry: 1.uint64,
duration: 1'StorageDuration,
pricePerBytePerSecond: 1'TokensPerSecond,
collateral: 1'Tokens,
expiry: 1'StorageTimestamp,
seen: false,
)
@ -314,14 +311,14 @@ suite "Slot queue":
test "expands available all possible slot indices on init":
let request = StorageRequest.example
let items = SlotQueueItem.init(request, request.ask.collateralPerSlot.stuint(256))
let items = SlotQueueItem.init(request, request.ask.collateralPerSlot)
check items.len.uint64 == request.ask.slots
var checked = 0
for slotIndex in 0'u16 ..< request.ask.slots.uint16:
check items.anyIt(
it ==
SlotQueueItem.init(
request, slotIndex, request.ask.collateralPerSlot.stuint(256)
request, slotIndex, request.ask.collateralPerSlot
)
)
inc checked
@ -357,8 +354,8 @@ suite "Slot queue":
let items = SlotQueueItem.init(
request.id,
request.ask,
request.expiry.u64,
request.ask.collateralPerSlot.stuint(256),
0'StorageTimestamp,
request.ask.collateralPerSlot,
)
check items.len.uint16 == maxUInt16
@ -371,8 +368,8 @@ suite "Slot queue":
discard SlotQueueItem.init(
request.id,
request.ask,
request.expiry.u64,
request.ask.collateralPerSlot.stuint(256),
0'StorageTimestamp,
request.ask.collateralPerSlot,
)
test "cannot push duplicate items":
@ -413,11 +410,11 @@ suite "Slot queue":
newSlotQueue(maxSize = 8, maxWorkers = 1, processSlotDelay = 10.millis)
let request0 = StorageRequest.example
var request1 = StorageRequest.example
request1.ask.collateralPerByte += 1.u128
request1.ask.collateralPerByte += 1'Tokens
let items0 =
SlotQueueItem.init(request0, request0.ask.collateralPerSlot.stuint(256))
SlotQueueItem.init(request0, request0.ask.collateralPerSlot)
let items1 =
SlotQueueItem.init(request1, request1.ask.collateralPerSlot.stuint(256))
SlotQueueItem.init(request1, request1.ask.collateralPerSlot)
check queue.push(items0).isOk
check queue.push(items1).isOk
let last = items1[items1.high]
@ -429,11 +426,11 @@ suite "Slot queue":
newSlotQueue(maxSize = 8, maxWorkers = 1, processSlotDelay = 10.millis)
let request0 = StorageRequest.example
var request1 = StorageRequest.example
request1.ask.collateralPerByte += 1.u128
request1.ask.collateralPerByte += 1'Tokens
let items0 =
SlotQueueItem.init(request0, request0.ask.collateralPerSlot.stuint(256))
SlotQueueItem.init(request0, request0.ask.collateralPerSlot)
let items1 =
SlotQueueItem.init(request1, request1.ask.collateralPerSlot.stuint(256))
SlotQueueItem.init(request1, request1.ask.collateralPerSlot)
check queue.push(items0).isOk
check queue.push(items1).isOk
queue.delete(request1.id)
@ -453,17 +450,17 @@ suite "Slot queue":
request4.ask.collateralPerByte = request3.ask.collateralPerByte + 1
request5.ask.collateralPerByte = request4.ask.collateralPerByte + 1
let item0 =
SlotQueueItem.init(request0, 0, request0.ask.collateralPerSlot.stuint(256))
SlotQueueItem.init(request0, 0, request0.ask.collateralPerSlot)
let item1 =
SlotQueueItem.init(request1, 0, request1.ask.collateralPerSlot.stuint(256))
SlotQueueItem.init(request1, 0, request1.ask.collateralPerSlot)
let item2 =
SlotQueueItem.init(request2, 0, request2.ask.collateralPerSlot.stuint(256))
SlotQueueItem.init(request2, 0, request2.ask.collateralPerSlot)
let item3 =
SlotQueueItem.init(request3, 0, request3.ask.collateralPerSlot.stuint(256))
SlotQueueItem.init(request3, 0, request3.ask.collateralPerSlot)
let item4 =
SlotQueueItem.init(request4, 0, request4.ask.collateralPerSlot.stuint(256))
SlotQueueItem.init(request4, 0, request4.ask.collateralPerSlot)
let item5 =
SlotQueueItem.init(request5, 0, request5.ask.collateralPerSlot.stuint(256))
SlotQueueItem.init(request5, 0, request5.ask.collateralPerSlot)
check queue.contains(item5) == false
check queue.push(@[item0, item1, item2, item3, item4, item5]).isOk
check queue.contains(item5)
@ -471,37 +468,36 @@ suite "Slot queue":
test "sorts items by profitability descending (higher pricePerBytePerSecond == higher priority == goes first in the list)":
var request = StorageRequest.example
let item0 =
SlotQueueItem.init(request, 0, request.ask.collateralPerSlot.stuint(256))
request.ask.pricePerBytePerSecond += 1.stuint(96)
SlotQueueItem.init(request, 0, request.ask.collateralPerSlot)
request.ask.pricePerBytePerSecond += 1'TokensPerSecond
let item1 =
SlotQueueItem.init(request, 1, request.ask.collateralPerSlot.stuint(256))
SlotQueueItem.init(request, 1, request.ask.collateralPerSlot)
check item1 < item0
test "sorts items by collateral ascending (higher required collateral = lower priority == comes later in the list)":
var request = StorageRequest.example
let item0 =
SlotQueueItem.init(request, 0, request.ask.collateralPerSlot.stuint(256))
SlotQueueItem.init(request, 0, request.ask.collateralPerSlot)
let item1 = SlotQueueItem.init(
request, 1, request.ask.collateralPerSlot.stuint(256) + 1.u256
request, 1, request.ask.collateralPerSlot + 1'Tokens
)
check item1 > item0
test "sorts items by expiry descending (longer expiry = higher priority)":
var request = StorageRequest.example
let item0 =
SlotQueueItem.init(request, 0, request.ask.collateralPerSlot.stuint(256))
request.expiry += 1.stuint(40)
SlotQueueItem.init(request.id, 0, request.ask, 3'StorageTimestamp, request.ask.collateralPerSlot)
let item1 =
SlotQueueItem.init(request, 1, request.ask.collateralPerSlot.stuint(256))
SlotQueueItem.init(request.id, 1, request.ask, 7'StorageTimestamp, request.ask.collateralPerSlot)
check item1 < item0
test "sorts items by slot size descending (bigger dataset = higher profitability = higher priority)":
var request = StorageRequest.example
let item0 =
SlotQueueItem.init(request, 0, request.ask.collateralPerSlot.stuint(256))
SlotQueueItem.init(request, 0, request.ask.collateralPerSlot)
request.ask.slotSize += 1
let item1 =
SlotQueueItem.init(request, 1, request.ask.collateralPerSlot.stuint(256))
SlotQueueItem.init(request, 1, request.ask.collateralPerSlot)
check item1 < item0
test "should call callback once an item is added":
@ -523,16 +519,16 @@ suite "Slot queue":
# calling the callback for each pushed/updated item
var request = StorageRequest.example
let item0 =
SlotQueueItem.init(request, 0, request.ask.collateralPerSlot.stuint(256))
request.ask.pricePerBytePerSecond += 1.stuint(96)
SlotQueueItem.init(request, 0, request.ask.collateralPerSlot)
request.ask.pricePerBytePerSecond += 1'TokensPerSecond
let item1 =
SlotQueueItem.init(request, 1, request.ask.collateralPerSlot.stuint(256))
request.ask.pricePerBytePerSecond += 1.stuint(96)
SlotQueueItem.init(request, 1, request.ask.collateralPerSlot)
request.ask.pricePerBytePerSecond += 1'TokensPerSecond
let item2 =
SlotQueueItem.init(request, 2, request.ask.collateralPerSlot.stuint(256))
request.ask.pricePerBytePerSecond += 1.stuint(96)
SlotQueueItem.init(request, 2, request.ask.collateralPerSlot)
request.ask.pricePerBytePerSecond += 1'TokensPerSecond
let item3 =
SlotQueueItem.init(request, 3, request.ask.collateralPerSlot.stuint(256))
SlotQueueItem.init(request, 3, request.ask.collateralPerSlot)
check queue.push(item0).isOk
await sleepAsync(1.millis)
@ -558,16 +554,16 @@ suite "Slot queue":
# calling the callback for each pushed/updated item
var request = StorageRequest.example
let item0 =
SlotQueueItem.init(request, 0, request.ask.collateralPerSlot.stuint(256))
request.ask.pricePerBytePerSecond += 1.stuint(96)
SlotQueueItem.init(request, 0, request.ask.collateralPerSlot)
request.ask.pricePerBytePerSecond += 1'TokensPerSecond
let item1 =
SlotQueueItem.init(request, 1, request.ask.collateralPerSlot.stuint(256))
request.ask.pricePerBytePerSecond += 1.stuint(96)
SlotQueueItem.init(request, 1, request.ask.collateralPerSlot)
request.ask.pricePerBytePerSecond += 1'TokensPerSecond
let item2 =
SlotQueueItem.init(request, 2, request.ask.collateralPerSlot.stuint(256))
request.ask.pricePerBytePerSecond += 1.stuint(96)
SlotQueueItem.init(request, 2, request.ask.collateralPerSlot)
request.ask.pricePerBytePerSecond += 1'TokensPerSecond
let item3 =
SlotQueueItem.init(request, 3, request.ask.collateralPerSlot.stuint(256))
SlotQueueItem.init(request, 3, request.ask.collateralPerSlot)
check queue.push(item0).isOk
check queue.push(item1).isOk
@ -591,7 +587,7 @@ suite "Slot queue":
queue.pause
let request = StorageRequest.example
var items = SlotQueueItem.init(request, request.ask.collateralPerSlot.stuint(256))
var items = SlotQueueItem.init(request, request.ask.collateralPerSlot)
check queue.push(items).isOk
# check all items processed
check eventually queue.len == 0
@ -603,8 +599,8 @@ suite "Slot queue":
request.id,
0'u16,
request.ask,
request.expiry.u64,
request.ask.collateralPerSlot.stuint(256),
0'StorageTimestamp,
request.ask.collateralPerSlot,
seen = true,
)
check queue.paused
@ -618,8 +614,8 @@ suite "Slot queue":
request.id,
1'u16,
request.ask,
request.expiry.u64,
request.ask.collateralPerSlot.stuint(256),
0'StorageTimestamp,
request.ask.collateralPerSlot,
seen = false,
)
check queue.paused
@ -636,16 +632,16 @@ suite "Slot queue":
request.id,
0'u16,
request.ask,
request.expiry.u64,
request.ask.collateralPerSlot.stuint(256),
0'StorageTimestamp,
request.ask.collateralPerSlot,
seen = false,
)
let seen = SlotQueueItem.init(
request.id,
1'u16,
request.ask,
request.expiry.u64,
request.ask.collateralPerSlot.stuint(256),
0'StorageTimestamp,
request.ask.collateralPerSlot,
seen = true,
)
# push causes unpause
@ -664,16 +660,16 @@ suite "Slot queue":
request.id,
0'u16,
request.ask,
request.expiry.u64,
request.ask.collateralPerSlot.stuint(256),
0'StorageTimestamp,
request.ask.collateralPerSlot,
seen = false,
)
let seen = SlotQueueItem.init(
request.id,
1'u16,
request.ask,
request.expiry.u64,
request.ask.collateralPerSlot.stuint(256),
0'StorageTimestamp,
request.ask.collateralPerSlot,
seen = true,
)
# push seen item to ensure that queue is pausing
@ -696,16 +692,16 @@ suite "Slot queue":
request.id,
0'u16,
request.ask,
request.expiry.u64,
request.ask.collateralPerSlot.stuint(256),
0'StorageTimestamp,
request.ask.collateralPerSlot,
seen = true,
)
let item1 = SlotQueueItem.init(
request.id,
1'u16,
request.ask,
request.expiry.u64,
request.ask.collateralPerSlot.stuint(256),
0'StorageTimestamp,
request.ask.collateralPerSlot,
seen = true,
)
check queue.push(item0).isOk

View File

@ -29,8 +29,8 @@ asyncchecksuite "Purchasing":
ask: StorageAsk(
slots: uint8.example.uint64,
slotSize: uint32.example.uint64,
duration: uint16.example.stuint(40),
pricePerBytePerSecond: uint8.example.stuint(96),
duration: StorageDuration.init(uint16.example),
pricePerBytePerSecond: TokensPerSecond.init(uint8.example),
)
)
@ -82,36 +82,34 @@ asyncchecksuite "Purchasing":
check market.requested[0].client == await market.getSigner()
test "succeeds when request is finished":
market.requestExpiry[populatedRequest.id] = getTime().toUnix() + 10
let expiry = StorageTimestamp.init(getTime().toUnix() + 10)
market.requestExpiry[populatedRequest.id] = expiry
let purchase = await purchasing.purchase(populatedRequest)
check eventually market.requested.len > 0
let request = market.requested[0]
let requestEnd = getTime().toUnix() + 42
let requestEnd = StorageTimestamp.init(getTime().toUnix() + 42)
market.requestEnds[request.id] = requestEnd
market.emitRequestFulfilled(request.id)
clock.set(requestEnd + 1)
clock.set(requestEnd.toSecondsSince1970 + 1)
await purchase.wait()
check purchase.error.isNone
test "fails when request times out":
let expiry = getTime().toUnix() + 10
market.requestExpiry[populatedRequest.id] = expiry
let purchase = await purchasing.purchase(populatedRequest)
check eventually market.requested.len > 0
clock.set(expiry + 1)
let expiry = market.requestExpiry[populatedRequest.id]
clock.set(expiry.toSecondsSince1970 + 1)
expect PurchaseTimeout:
await purchase.wait()
test "checks that funds were withdrawn when purchase times out":
let expiry = getTime().toUnix() + 10
market.requestExpiry[populatedRequest.id] = expiry
let purchase = await purchasing.purchase(populatedRequest)
check eventually market.requested.len > 0
let request = market.requested[0]
clock.set(expiry + 1)
let expiry = market.requestExpiry[populatedRequest.id]
clock.set(expiry.toSecondsSince1970 + 1)
expect PurchaseTimeout:
await purchase.wait()
check market.withdrawn == @[request.id]
@ -130,8 +128,8 @@ suite "Purchasing state machine":
ask: StorageAsk(
slots: uint8.example.uint64,
slotSize: uint32.example.uint64,
duration: uint16.example.stuint(40),
pricePerBytePerSecond: uint8.example.stuint(96),
duration: StorageDuration.init(uint16.example),
pricePerBytePerSecond: TokensPerSecond.init(uint8.example),
)
)
@ -158,7 +156,7 @@ suite "Purchasing state machine":
market.requestState[request5.id] = RequestState.Failed
# ensure the started state doesn't error, giving a false positive test result
market.requestEnds[request2.id] = clock.now() - 1
market.requestEnds[request2.id] = StorageTimestamp.init(clock.now() - 1)
await purchasing.load()
check eventually purchasing.getPurchase(PurchaseId(request1.id)) .? finished ==
@ -184,8 +182,8 @@ suite "Purchasing state machine":
test "moves to PurchaseStarted when request state is Started":
let request = StorageRequest.example
let purchase = Purchase.new(request, market, clock)
let duration = request.ask.duration.toSecondsSince1970
market.requestEnds[request.id] = clock.now() + duration
let duration = request.ask.duration
market.requestEnds[request.id] = StorageTimestamp.init(clock.now()) + duration
market.requested = @[request]
market.requestState[request.id] = RequestState.Started
let next = await PurchaseUnknown().run(purchase)
@ -218,8 +216,8 @@ suite "Purchasing state machine":
test "moves to PurchaseFailed state once RequestFailed emitted":
let request = StorageRequest.example
let purchase = Purchase.new(request, market, clock)
let duration = request.ask.duration.toSecondsSince1970
market.requestEnds[request.id] = clock.now() + duration
let duration = request.ask.duration
market.requestEnds[request.id] = StorageTimestamp.init(clock.now) + duration
let future = PurchaseStarted().run(purchase)
market.emitRequestFailed(request.id)
@ -230,11 +228,11 @@ suite "Purchasing state machine":
test "moves to PurchaseFinished state once request finishes":
let request = StorageRequest.example
let purchase = Purchase.new(request, market, clock)
let duration = request.ask.duration.toSecondsSince1970
market.requestEnds[request.id] = clock.now() + duration
let duration = request.ask.duration
market.requestEnds[request.id] = StorageTimestamp.init(clock.now) + duration
let future = PurchaseStarted().run(purchase)
clock.advance(duration + 1)
clock.advance(duration.u64.int64 + 1)
let next = await future
check !next of PurchaseFinished

View File

@ -2,8 +2,8 @@ import pkg/chronos
import std/strformat
import std/times
import codex/contracts/periods
import codex/validation
import codex/periods
import codex/clock
import ../asynctest
@ -16,8 +16,8 @@ logScope:
topics = "testValidation"
asyncchecksuite "validation":
let period = 10.uint64
let timeout = 5.uint64
let period = 10'StorageDuration
let timeout = 5'StorageDuration
let maxSlots = MaxSlots(100)
let validationGroups = ValidationGroups(8).some
let slot = Slot.example
@ -50,9 +50,9 @@ asyncchecksuite "validation":
setup:
groupIndex = groupIndexForSlotId(slot.id, !validationGroups)
clock = MockClock.new()
market = MockMarket.new(clock = Clock(clock).some)
market.config.proofs.period = period.stuint(40)
market.config.proofs.timeout = timeout.stuint(40)
market = MockMarket.new(clock)
market.config.proofs.period = period
market.config.proofs.timeout = timeout
validation = newValidation(clock, market, maxSlots, validationGroups, groupIndex)
teardown:
@ -61,7 +61,7 @@ asyncchecksuite "validation":
proc advanceToNextPeriod() =
let periodicity = Periodicity(seconds: period)
let period = periodicity.periodOf(clock.now().Timestamp)
let period = periodicity.periodOf(StorageTimestamp.init(clock.now()))
let periodEnd = periodicity.periodEnd(period)
clock.set(periodEnd.toSecondsSince1970 + 1)

View File

@ -15,10 +15,10 @@ ethersuite "Marketplace contracts":
var periodicity: Periodicity
var request: StorageRequest
var slotId: SlotId
var filledAt: UInt256
var filledAt: StorageTimestamp
proc expectedPayout(endTimestamp: UInt256): UInt256 =
return (endTimestamp - filledAt) * request.ask.pricePerSlotPerSecond.stuint(256)
proc expectedPayout(endTimestamp: StorageTimestamp): Tokens =
return request.ask.pricePerSlotPerSecond * filledAt.until(endTimestamp)
proc switchAccount(account: Signer) =
marketplace = marketplace.connect(account)
@ -35,25 +35,26 @@ ethersuite "Marketplace contracts":
token = Erc20Token.new(tokenAddress, ethProvider.getSigner())
let config = await marketplace.configuration()
periodicity = Periodicity(seconds: config.proofs.period.u64)
periodicity = Periodicity(seconds: config.proofs.period)
request = StorageRequest.example
request.client = await client.getAddress()
switchAccount(client)
discard await token.approve(marketplace.address, request.totalPrice.stuint(256)).confirm(1)
discard await token.approve(marketplace.address, request.totalPrice.u256).confirm(1)
discard await marketplace.requestStorage(request).confirm(1)
switchAccount(host)
discard
await token.approve(marketplace.address, request.ask.collateralPerSlot.stuint(256)).confirm(1)
await token.approve(marketplace.address, request.ask.collateralPerSlot.u256).confirm(1)
discard await marketplace.reserveSlot(request.id, 0.uint64).confirm(1)
let receipt = await marketplace.fillSlot(request.id, 0.uint64, proof).confirm(1)
filledAt = await ethProvider.blockTime(BlockTag.init(!receipt.blockNumber))
let receiptTime = await ethProvider.blockTime(BlockTag.init(!receipt.blockNumber))
filledAt = StorageTimestamp.init(receiptTime.stuint(40))
slotId = request.slotId(0.uint64)
proc waitUntilProofRequired(slotId: SlotId) {.async.} =
let currentPeriod =
periodicity.periodOf((await ethProvider.currentTime()).truncate(uint64))
periodicity.periodOf((await ethProvider.currentTime()).truncate(int64))
await ethProvider.advanceTimeTo(periodicity.periodEnd(currentPeriod).u256)
while not (
(await marketplace.isProofRequired(slotId)) and
@ -65,7 +66,7 @@ ethersuite "Marketplace contracts":
proc startContract() {.async.} =
for slotIndex in 1 ..< request.ask.slots:
discard await token
.approve(marketplace.address, request.ask.collateralPerSlot.stuint(256))
.approve(marketplace.address, request.ask.collateralPerSlot.u256)
.confirm(1)
discard await marketplace.reserveSlot(request.id, slotIndex.uint64).confirm(1)
discard await marketplace.fillSlot(request.id, slotIndex.uint64, proof).confirm(1)
@ -79,11 +80,11 @@ ethersuite "Marketplace contracts":
switchAccount(host)
await waitUntilProofRequired(slotId)
let missingPeriod =
periodicity.periodOf((await ethProvider.currentTime()).truncate(uint64))
periodicity.periodOf((await ethProvider.currentTime()).truncate(int64))
let endOfPeriod = periodicity.periodEnd(missingPeriod)
await ethProvider.advanceTimeTo(endOfPeriod.u256 + 1)
switchAccount(client)
discard await marketplace.markProofAsMissing(slotId, missingPeriod.stuint(40)).confirm(1)
discard await marketplace.markProofAsMissing(slotId, missingPeriod).confirm(1)
test "can be paid out at the end":
switchAccount(host)
@ -94,15 +95,14 @@ ethersuite "Marketplace contracts":
let startBalance = await token.balanceOf(address)
discard await marketplace.freeSlot(slotId).confirm(1)
let endBalance = await token.balanceOf(address)
check endBalance ==
(startBalance + expectedPayout(requestEnd.u256) + request.ask.collateralPerSlot.stuint(256))
check (endBalance - startBalance) == (expectedPayout(requestEnd) + request.ask.collateralPerSlot).u256
test "cannot mark proofs missing for cancelled request":
let expiry = await marketplace.requestExpiry(request.id)
await ethProvider.advanceTimeTo((expiry + 1).u256)
switchAccount(client)
let missingPeriod =
periodicity.periodOf((await ethProvider.currentTime()).truncate(uint64))
periodicity.periodOf((await ethProvider.currentTime()).truncate(int64))
await ethProvider.advanceTime(periodicity.seconds.u256)
expect Marketplace_SlotNotAcceptingProofs:
discard await marketplace.markProofAsMissing(slotId, missingPeriod.stuint(40)).confirm(1)
discard await marketplace.markProofAsMissing(slotId, missingPeriod).confirm(1)

View File

@ -30,10 +30,8 @@ ethersuite "On-Chain Market":
var host: Signer
var otherHost: Signer
proc expectedPayout(
r: StorageRequest, startTimestamp: UInt256, endTimestamp: UInt256
): UInt256 =
return (endTimestamp - startTimestamp) * r.ask.pricePerSlotPerSecond.stuint(256)
proc expectedPayout(request: StorageRequest, start, finish: StorageTimestamp): Tokens =
return request.ask.pricePerSlotPerSecond * start.until(finish)
proc switchAccount(account: Signer) {.async.} =
marketplace = marketplace.connect(account)
@ -49,7 +47,7 @@ ethersuite "On-Chain Market":
let tokenAddress = await marketplace.token()
token = Erc20Token.new(tokenAddress, ethProvider.getSigner())
periodicity = Periodicity(seconds: config.proofs.period.u64)
periodicity = Periodicity(seconds: config.proofs.period)
request = StorageRequest.example
request.client = accounts[0]
@ -60,7 +58,7 @@ ethersuite "On-Chain Market":
proc advanceToNextPeriod() {.async.} =
let currentPeriod =
periodicity.periodOf((await ethProvider.currentTime()).truncate(uint64))
periodicity.periodOf((await ethProvider.currentTime()).truncate(int64))
await ethProvider.advanceTimeTo((periodicity.periodEnd(currentPeriod) + 1).u256)
proc advanceToCancelledRequest(request: StorageRequest) {.async.} =
@ -88,12 +86,12 @@ ethersuite "On-Chain Market":
let periodicity = market.periodicity
let config = await marketplace.configuration()
let periodLength = config.proofs.period
check periodicity.seconds == periodLength.u64
check periodicity.seconds == periodLength
test "can retrieve proof timeout":
let proofTimeout = market.proofTimeout
let config = await marketplace.configuration()
check proofTimeout == config.proofs.timeout.u64
check proofTimeout == config.proofs.timeout
test "supports marketplace requests":
await market.requestStorage(request)
@ -114,12 +112,12 @@ ethersuite "On-Chain Market":
let endBalanceClient = await token.balanceOf(clientAddress)
check endBalanceClient == (startBalanceClient + request.totalPrice.stuint(256))
check endBalanceClient == (startBalanceClient + request.totalPrice.u256)
test "supports request subscriptions":
var receivedIds: seq[RequestId]
var receivedAsks: seq[StorageAsk]
proc onRequest(id: RequestId, ask: StorageAsk, expiry: uint64) =
proc onRequest(id: RequestId, ask: StorageAsk, expiry: StorageTimestamp) =
receivedIds.add(id)
receivedAsks.add(ask)
@ -168,7 +166,7 @@ ethersuite "On-Chain Market":
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateralPerSlot)
await waitUntilProofRequired(slotId)
let missingPeriod =
periodicity.periodOf((await ethProvider.currentTime()).truncate(uint64))
periodicity.periodOf((await ethProvider.currentTime()).truncate(int64))
await advanceToNextPeriod()
await market.markProofAsMissing(slotId, missingPeriod)
check (await marketplace.missingProofs(slotId)) == 1
@ -180,7 +178,7 @@ ethersuite "On-Chain Market":
await market.fillSlot(request.id, slotIndex, proof, request.ask.collateralPerSlot)
await waitUntilProofRequired(slotId)
let missingPeriod =
periodicity.periodOf((await ethProvider.currentTime()).truncate(uint64))
periodicity.periodOf((await ethProvider.currentTime()).truncate(int64))
await advanceToNextPeriod()
check (await market.canProofBeMarkedAsMissing(slotId, missingPeriod)) == true
@ -324,9 +322,9 @@ ethersuite "On-Chain Market":
break
await waitUntilProofRequired(slotId)
let missingPeriod =
periodicity.periodOf((await ethProvider.currentTime()).truncate(uint64))
periodicity.periodOf((await ethProvider.currentTime()).truncate(int64))
await advanceToNextPeriod()
discard await marketplace.markProofAsMissing(slotId, missingPeriod.stuint(40)).confirm(1)
discard await marketplace.markProofAsMissing(slotId, missingPeriod).confirm(1)
check eventually receivedIds == @[request.id]
await subscription.unsubscribe()
@ -510,7 +508,8 @@ ethersuite "On-Chain Market":
await switchAccount(host)
await market.reserveSlot(request.id, 0.uint64)
await market.fillSlot(request.id, 0.uint64, proof, request.ask.collateralPerSlot)
let filledAt = await ethProvider.blockTime(BlockTag.latest)
let blockTime = await ethProvider.blockTime(BlockTag.latest)
let filledAt = StorageTimestamp.init(blockTime.stuint(40))
for slotIndex in 1 ..< request.ask.slots:
await market.reserveSlot(request.id, slotIndex.uint64)
@ -525,8 +524,8 @@ ethersuite "On-Chain Market":
await market.freeSlot(request.slotId(0.uint64))
let endBalance = await token.balanceOf(address)
let expectedPayout = request.expectedPayout(filledAt, requestEnd.u256)
check endBalance == (startBalance + expectedPayout + request.ask.collateralPerSlot.stuint(256))
let expectedPayout = request.expectedPayout(filledAt, requestEnd)
check (endBalance - startBalance) == (expectedPayout + request.ask.collateralPerSlot).u256
test "the request is added to cache after the first access":
await market.requestStorage(request)

View File

@ -37,33 +37,39 @@ proc example*[T](_: type seq[T]): seq[T] =
let length = uint8.example.int
newSeqWith(length, T.example)
proc example*(_: type UInt256): UInt256 =
UInt256.fromBytes(array[32, byte].example)
proc example*(_: type UInt128): UInt128 =
UInt128.fromBytes(array[16, byte].example)
proc example*[bits](_: type StUint[bits]): StUint[bits] =
StUint[bits].fromBytes(array[bits div 8, byte].example)
proc example*[T: distinct](_: type T): T =
type baseType = T.distinctBase
T(baseType.example)
proc example*(_: type StorageDuration): StorageDuration =
StorageDuration.init(StUint[40].example)
proc example*(_: type TokensPerSecond): TokensPerSecond =
TokensPerSecond.init(StUint[96].example)
proc example*(_: type Tokens): Tokens =
Tokens.init(UInt128.example)
proc example*(_: type StorageRequest): StorageRequest =
StorageRequest(
client: Address.example,
ask: StorageAsk(
slots: 4,
slotSize: (1 * 1024 * 1024 * 1024).uint64, # 1 Gigabyte
duration: (10 * 60 * 60).stuint(40), # 10 hours
collateralPerByte: 1.u128,
duration: StorageDuration.init((10 * 60 * 60).stuint(40)), # 10 hours
collateralPerByte: 1'Tokens,
proofProbability: 4.u256, # require a proof roughly once every 4 periods
pricePerBytePerSecond: 1.stuint(96),
pricePerBytePerSecond: 1'TokensPerSecond,
maxSlotLoss: 2, # 2 slots can be freed without data considered to be lost
),
content: StorageContent(
cid: Cid.init("zb2rhheVmk3bLks5MgzTqyznLu1zqGH5jrfTA1eAZXrjx7Vob").tryGet,
merkleRoot: array[32, byte].example,
),
expiry: (60 * 60).stuint(40), # 1 hour ,
expiry: StorageDuration.init((60 * 60).stuint(40)), # 1 hour ,
nonce: Nonce.example,
)
@ -75,7 +81,7 @@ proc example*(_: type Slot): Slot =
proc example*(_: type SlotQueueItem): SlotQueueItem =
let request = StorageRequest.example
let slot = Slot.example
let collateral = request.ask.collateralPerSlot.stuint(256)
let collateral = request.ask.collateralPerSlot
SlotQueueItem.init(request, slot.slotIndex.uint16, collateral)
proc example(_: type G1Point): G1Point =

View File

@ -217,16 +217,14 @@ proc space*(
proc requestStorageRaw*(
client: CodexClient,
cid: Cid,
duration: StUint[40],
pricePerBytePerSecond: StUint[96],
duration: StorageDuration,
pricePerBytePerSecond: TokensPerSecond,
proofProbability: UInt256,
collateralPerByte: UInt128,
expiry = 0.stuint(40),
collateralPerByte: Tokens,
expiry = none StorageDuration,
nodes: uint = 3,
tolerance: uint = 1,
): Future[HttpClientResponseRef] {.
async: (raw: true, raises: [CancelledError, HttpError])
.} =
): Future[HttpClientResponseRef] {.async: (raises: [CancelledError, HttpError]).} =
## Call request storage REST endpoint
##
let url = client.baseurl & "/storage/request/" & $cid
@ -240,19 +238,35 @@ proc requestStorageRaw*(
"tolerance": tolerance,
}
if expiry != 0.stuint(40):
json["expiry"] = %($expiry)
if expiry =? expiry:
json["expiry"] = %expiry
return client.post(url, $json)
return await client.post(url, $json)
proc requestStorageRaw*(
client: CodexClient,
cid: Cid,
duration: StorageDuration,
pricePerBytePerSecond: TokensPerSecond,
proofProbability: UInt256,
collateralPerByte: Tokens,
expiry: StorageDuration,
nodes: uint = 3,
tolerance: uint = 1,
): Future[HttpClientResponseRef] {.async: (raises: [CancelledError, HttpError]).} =
return await client.requestStorageRaw(
cid, duration, pricePerBytePerSecond, proofProbability, collateralPerByte,
some expiry, nodes, tolerance
)
proc requestStorage*(
client: CodexClient,
cid: Cid,
duration: StUint[40],
pricePerBytePerSecond: StUint[96],
duration: StorageDuration,
pricePerBytePerSecond: TokensPerSecond,
proofProbability: UInt256,
expiry: StUint[40],
collateralPerByte: UInt128,
expiry = none StorageDuration,
collateralPerByte: Tokens,
nodes: uint = 3,
tolerance: uint = 1,
): Future[?!PurchaseId] {.async: (raises: [CancelledError, HttpError]).} =
@ -269,6 +283,22 @@ proc requestStorage*(
doAssert(false, body)
PurchaseId.fromHex(body).catch
proc requestStorage*(
client: CodexClient,
cid: Cid,
duration: StorageDuration,
pricePerBytePerSecond: TokensPerSecond,
proofProbability: UInt256,
expiry: StorageDuration,
collateralPerByte: Tokens,
nodes: uint = 3,
tolerance: uint = 1,
): Future[?!PurchaseId] {.async: (raises: [CancelledError, HttpError]).} =
return await client.requestStorage(
cid, duration, pricePerBytePerSecond, proofProbability, some expiry,
collateralPerByte, nodes, tolerance
)
proc getPurchase*(
client: CodexClient, purchaseId: PurchaseId
): Future[?!RestPurchase] {.async: (raises: [CancelledError, HttpError]).} =
@ -291,10 +321,12 @@ proc getSalesAgent*(
proc postAvailabilityRaw*(
client: CodexClient,
totalSize, duration: uint64,
minPricePerBytePerSecond, totalCollateral: UInt256,
totalSize: uint64,
duration: StorageDuration,
minPricePerBytePerSecond: TokensPerSecond,
totalCollateral: Tokens,
enabled: ?bool = bool.none,
until: ?SecondsSince1970 = SecondsSince1970.none,
until = StorageTimestamp.none,
): Future[HttpClientResponseRef] {.async: (raises: [CancelledError, HttpError]).} =
## Post sales availability endpoint
##
@ -312,10 +344,12 @@ proc postAvailabilityRaw*(
proc postAvailability*(
client: CodexClient,
totalSize, duration: uint64,
minPricePerBytePerSecond, totalCollateral: UInt256,
totalSize: uint64,
duration: StorageDuration,
minPricePerBytePerSecond: TokensPerSecond,
totalCollateral: Tokens,
enabled: ?bool = bool.none,
until: ?SecondsSince1970 = SecondsSince1970.none,
until = StorageTimestamp.none,
): Future[?!Availability] {.async: (raises: [CancelledError, HttpError]).} =
let response = await client.postAvailabilityRaw(
totalSize = totalSize,
@ -335,10 +369,13 @@ proc postAvailability*(
proc patchAvailabilityRaw*(
client: CodexClient,
availabilityId: AvailabilityId,
totalSize, freeSize, duration: ?uint64 = uint64.none,
minPricePerBytePerSecond, totalCollateral: ?UInt256 = UInt256.none,
totalSize = none uint64,
freeSize = none uint64,
duration = none StorageDuration,
minPricePerBytePerSecond = none TokensPerSecond,
enabled: ?bool = bool.none,
until: ?SecondsSince1970 = SecondsSince1970.none,
until = StorageTimestamp.none,
totalCollateral = none Tokens,
): Future[HttpClientResponseRef] {.
async: (raw: true, raises: [CancelledError, HttpError])
.} =
@ -375,10 +412,12 @@ proc patchAvailabilityRaw*(
proc patchAvailability*(
client: CodexClient,
availabilityId: AvailabilityId,
totalSize, duration: ?uint64 = uint64.none,
minPricePerBytePerSecond, totalCollateral: ?UInt256 = UInt256.none,
totalSize = none uint64,
duration = none StorageDuration,
minPricePerBytePerSecond = none TokensPerSecond,
totalCollateral = none Tokens,
enabled: ?bool = bool.none,
until: ?SecondsSince1970 = SecondsSince1970.none,
until = StorageTimestamp.none,
): Future[void] {.async: (raises: [CancelledError, HttpError]).} =
let response = await client.patchAvailabilityRaw(
availabilityId,

View File

@ -2,7 +2,7 @@ import pkg/chronos
import pkg/ethers/erc20
from pkg/libp2p import Cid
import pkg/codex/contracts/marketplace as mp
import pkg/codex/periods
import pkg/codex/contracts/periods
import pkg/codex/utils/json
from pkg/codex/utils import roundUp, divUp
import ./multinodes
@ -15,21 +15,21 @@ export multinodes
template marketplacesuite*(name: string, body: untyped) =
multinodesuite name:
var marketplace {.inject, used.}: Marketplace
var period: uint64
var period: StorageDuration
var periodicity: Periodicity
var token {.inject, used.}: Erc20Token
proc getCurrentPeriod(): Future[Period] {.async.} =
return periodicity.periodOf((await ethProvider.currentTime()).truncate(uint64))
proc getCurrentPeriod(): Future[ProofPeriod] {.async.} =
return periodicity.periodOf((await ethProvider.currentTime()).truncate(int64))
proc advanceToNextPeriod() {.async.} =
let periodicity = Periodicity(seconds: period)
let currentTime = (await ethProvider.currentTime()).truncate(uint64)
let currentTime = (await ethProvider.currentTime()).truncate(int64)
let currentPeriod = periodicity.periodOf(currentTime)
let endOfPeriod = periodicity.periodEnd(currentPeriod)
await ethProvider.advanceTimeTo(endOfPeriod.u256 + 1)
template eventuallyP(condition: untyped, finalPeriod: Period): bool =
template eventuallyP(condition: untyped, finalPeriod: ProofPeriod): bool =
proc eventuallyP(): Future[bool] {.async.} =
while (
let currentPeriod = await getCurrentPeriod()
@ -43,32 +43,32 @@ template marketplacesuite*(name: string, body: untyped) =
await eventuallyP()
proc periods(p: int): uint64 =
p.uint64 * period
proc periods(p: int): StorageDuration =
period * p.uint32
proc slotSize(blocks, nodes, tolerance: int): UInt256 =
proc slotSize(blocks, nodes, tolerance: int): uint64 =
let ecK = nodes - tolerance
let blocksRounded = roundUp(blocks, ecK)
let blocksPerSlot = divUp(blocksRounded, ecK)
(DefaultBlockSize * blocksPerSlot.NBytes).Natural.u256
(DefaultBlockSize * blocksPerSlot.NBytes).Natural.uint64
proc datasetSize(blocks, nodes, tolerance: int): UInt256 =
return nodes.u256 * slotSize(blocks, nodes, tolerance)
proc datasetSize(blocks, nodes, tolerance: int): uint64 =
return nodes.uint64 * slotSize(blocks, nodes, tolerance)
proc createAvailabilities(
datasetSize: uint64,
duration: uint64,
collateralPerByte: UInt256,
minPricePerBytePerSecond: UInt256,
duration: StorageDuration,
collateralPerByte: Tokens,
minPricePerBytePerSecond: TokensPerSecond,
): Future[void] {.async: (raises: [CancelledError, HttpError, ConfigurationError]).} =
let totalCollateral = datasetSize.u256 * collateralPerByte
let totalCollateral = collateralPerByte * datasetSize
# post availability to each provider
for i in 0 ..< providers().len:
let provider = providers()[i].client
discard await provider.postAvailability(
totalSize = datasetSize,
duration = duration.uint64,
duration = duration,
minPricePerBytePerSecond = minPricePerBytePerSecond,
totalCollateral = totalCollateral,
)
@ -77,10 +77,10 @@ template marketplacesuite*(name: string, body: untyped) =
client: CodexClient,
cid: Cid,
proofProbability = 1.u256,
duration = 12.periods.stuint(40),
pricePerBytePerSecond = 1.stuint(96),
collateralPerByte = 1.u128,
expiry = 4.periods.stuint(40),
duration = 12.periods,
pricePerBytePerSecond = 1'TokensPerSecond,
collateralPerByte = 1'Tokens,
expiry = 4.periods,
nodes = providers().len,
tolerance = 0,
): Future[PurchaseId] {.async: (raises: [CancelledError, HttpError]).} =
@ -104,7 +104,7 @@ template marketplacesuite*(name: string, body: untyped) =
let tokenAddress = await marketplace.token()
token = Erc20Token.new(tokenAddress, ethProvider.getSigner())
let config = await marketplace.configuration()
period = config.proofs.period.u64
period = config.proofs.period
periodicity = Periodicity(seconds: period)
body

View File

@ -16,9 +16,9 @@ marketplacesuite "Bug #821 - node crashes during erasure coding":
providers: CodexConfigs.init(nodes = 0).some,
):
let
pricePerBytePerSecond = 1.u256
pricePerBytePerSecond = 1'TokensPerSecond
duration = 20.periods
collateralPerByte = 1.u256
collateralPerByte = 1'Tokens
expiry = 10.periods
data = await RandomChunker.example(blocks = 8)
client = clients()[0]
@ -36,15 +36,15 @@ marketplacesuite "Bug #821 - node crashes during erasure coding":
# client requests storage but requires multiple slots to host the content
discard await clientApi.requestStorage(
cid,
duration = duration.stuint(40),
pricePerBytePerSecond = pricePerBytePerSecond.stuint(96),
expiry = expiry.stuint(40),
collateralPerByte = collateralPerByte.stuint(128),
duration = duration,
pricePerBytePerSecond = pricePerBytePerSecond,
expiry = expiry,
collateralPerByte = collateralPerByte,
nodes = 3,
tolerance = 1,
)
check eventually(requestId.isSome, timeout = expiry.int * 1000)
check eventually(requestId.isSome, timeout = expiry.u64.int * 1000)
let
request = await marketplace.getRequest(requestId.get)

View File

@ -18,8 +18,8 @@ marketplacesuite "Marketplace":
var client: CodexClient
var clientAccount: Address
const minPricePerBytePerSecond = 1.u256
const collateralPerByte = 1.u128
const minPricePerBytePerSecond = 1'TokensPerSecond
const collateralPerByte = 1'Tokens
const blocks = 8
const ecNodes = 3
const ecTolerance = 1
@ -42,9 +42,9 @@ marketplacesuite "Marketplace":
let availability = (
await host.postAvailability(
totalSize = size,
duration = 20 * 60.uint64,
duration = StorageDuration.init(20'u32 * 60),
minPricePerBytePerSecond = minPricePerBytePerSecond,
totalCollateral = size.u256 * minPricePerBytePerSecond,
totalCollateral = collateralPerByte * size,
)
).get
@ -52,10 +52,10 @@ marketplacesuite "Marketplace":
let cid = (await client.upload(data)).get
let id = await client.requestStorage(
cid,
duration = 20 * 60.stuint(40),
pricePerBytePerSecond = minPricePerBytePerSecond.stuint(96),
duration = StorageDuration.init(20'u32 * 60),
pricePerBytePerSecond = minPricePerBytePerSecond,
proofProbability = 3.u256,
expiry = 10 * 60.stuint(40),
expiry = StorageDuration.init(10'u32 * 60),
collateralPerByte = collateralPerByte,
nodes = ecNodes,
tolerance = ecTolerance,
@ -82,7 +82,7 @@ marketplacesuite "Marketplace":
let marketplace = Marketplace.new(Marketplace.address, ethProvider.getSigner())
let tokenAddress = await marketplace.token()
let token = Erc20Token.new(tokenAddress, ethProvider.getSigner())
let duration = 20 * 60.uint64
let duration = StorageDuration.init(20'u32 * 60)
# host makes storage available
let startBalanceHost = await token.balanceOf(hostAccount)
@ -91,7 +91,7 @@ marketplacesuite "Marketplace":
totalSize = size,
duration = duration,
minPricePerBytePerSecond = minPricePerBytePerSecond,
totalCollateral = size.u256 * minPricePerBytePerSecond,
totalCollateral = collateralPerByte * size,
)
).get
@ -99,10 +99,10 @@ marketplacesuite "Marketplace":
let cid = (await client.upload(data)).get
let id = await client.requestStorage(
cid,
duration = duration.stuint(40),
pricePerBytePerSecond = minPricePerBytePerSecond.stuint(96),
duration = duration,
pricePerBytePerSecond = minPricePerBytePerSecond,
proofProbability = 3.u256,
expiry = (10 * 60).stuint(40),
expiry = StorageDuration.init(10'u32 * 60),
collateralPerByte = collateralPerByte,
nodes = ecNodes,
tolerance = ecTolerance,
@ -125,7 +125,7 @@ marketplacesuite "Marketplace":
let slotSize = slotSize(blocks, ecNodes, ecTolerance)
let pricePerSlotPerSecond = minPricePerBytePerSecond * slotSize
check eventually (await token.balanceOf(hostAccount)) - startBalanceHost >=
(duration - 5 * 60).u256 * pricePerSlotPerSecond * ecNodes.u256
(duration.u256 - 5 * 60) * pricePerSlotPerSecond.u256 * ecNodes.u256
# Checking that client node receives some funds back that were not used for the host nodes
check eventually(
@ -134,8 +134,8 @@ marketplacesuite "Marketplace":
)
marketplacesuite "Marketplace payouts":
const minPricePerBytePerSecond = 1.u256
const collateralPerByte = 1.u128
const minPricePerBytePerSecond = 1'TokensPerSecond
const collateralPerByte = 1'Tokens
const blocks = 8
const ecNodes = 3
const ecTolerance = 1
@ -167,14 +167,14 @@ marketplacesuite "Marketplace payouts":
# provider makes storage available
let datasetSize = datasetSize(blocks, ecNodes, ecTolerance)
let totalAvailabilitySize = (datasetSize div 2).truncate(uint64)
let totalAvailabilitySize = datasetSize div 2
discard await providerApi.postAvailability(
# make availability size small enough that we can't fill all the slots,
# thus causing a cancellation
totalSize = totalAvailabilitySize,
duration = duration.uint64,
duration = duration,
minPricePerBytePerSecond = minPricePerBytePerSecond,
totalCollateral = collateralPerByte.stuint(256) * totalAvailabilitySize.u256,
totalCollateral = collateralPerByte * totalAvailabilitySize,
)
let cid = (await clientApi.upload(data)).get
@ -189,16 +189,16 @@ marketplacesuite "Marketplace payouts":
# client requests storage but requires multiple slots to host the content
let id = await clientApi.requestStorage(
cid,
duration = duration.stuint(40),
pricePerBytePerSecond = minPricePerBytePerSecond.stuint(96),
expiry = expiry.stuint(40),
duration = duration,
pricePerBytePerSecond = minPricePerBytePerSecond,
expiry = expiry,
collateralPerByte = collateralPerByte,
nodes = ecNodes,
tolerance = ecTolerance,
)
# wait until one slot is filled
check eventually(slotIdxFilled.isSome, timeout = expiry.int * 1000)
check eventually(slotIdxFilled.isSome, timeout = expiry.u64.int64 * 1000)
let slotId = slotId(!(await clientApi.requestId(id)), !slotIdxFilled)
# wait until sale is cancelled
@ -213,7 +213,7 @@ marketplacesuite "Marketplace payouts":
check eventually (
let endBalanceProvider = (await token.balanceOf(provider.ethAccount))
endBalanceProvider > startBalanceProvider and
endBalanceProvider < startBalanceProvider + expiry.u256 * pricePerSlotPerSecond
endBalanceProvider < startBalanceProvider + expiry.u256 * pricePerSlotPerSecond.u256
)
check eventually(
(

View File

@ -14,8 +14,8 @@ logScope:
topics = "integration test proofs"
marketplacesuite "Hosts submit regular proofs":
const minPricePerBytePerSecond = 1.u256
const collateralPerByte = 1.u256
const minPricePerBytePerSecond = 1'TokensPerSecond
const collateralPerByte = 1'Tokens
const blocks = 8
const ecNodes = 3
const ecTolerance = 1
@ -43,7 +43,7 @@ marketplacesuite "Hosts submit regular proofs":
let datasetSize =
datasetSize(blocks = blocks, nodes = ecNodes, tolerance = ecTolerance)
await createAvailabilities(
datasetSize.truncate(uint64),
datasetSize,
duration,
collateralPerByte,
minPricePerBytePerSecond,
@ -53,8 +53,8 @@ marketplacesuite "Hosts submit regular proofs":
let purchaseId = await client0.requestStorage(
cid,
expiry = expiry.stuint(40),
duration = duration.stuint(40),
expiry = expiry,
duration = duration,
nodes = ecNodes,
tolerance = ecTolerance,
)
@ -65,7 +65,7 @@ marketplacesuite "Hosts submit regular proofs":
let slotSize = slotSize(blocks, ecNodes, ecTolerance)
check eventually(
await client0.purchaseStateIs(purchaseId, "started"), timeout = expiry.int * 1000
await client0.purchaseStateIs(purchaseId, "started"), timeout = expiry.u64.int * 1000
)
var proofWasSubmitted = false
@ -74,7 +74,7 @@ marketplacesuite "Hosts submit regular proofs":
let subscription = await marketplace.subscribe(ProofSubmitted, onProofSubmitted)
check eventually(proofWasSubmitted, timeout = (duration - expiry).int * 1000)
check eventually(proofWasSubmitted, timeout = (duration - expiry).u64.int * 1000)
await subscription.unsubscribe()
@ -84,8 +84,8 @@ marketplacesuite "Simulate invalid proofs":
# tightened so that they are showing, as an integration test, that specific
# proofs are being marked as missed by the validator.
const minPricePerBytePerSecond = 1.u256
const collateralPerByte = 1.u256
const minPricePerBytePerSecond = 1'TokensPerSecond
const collateralPerByte = 1'Tokens
const blocks = 8
const ecNodes = 3
const ecTolerance = 1
@ -120,7 +120,7 @@ marketplacesuite "Simulate invalid proofs":
let datasetSize =
datasetSize(blocks = blocks, nodes = ecNodes, tolerance = ecTolerance)
await createAvailabilities(
datasetSize.truncate(uint64),
datasetSize,
duration,
collateralPerByte,
minPricePerBytePerSecond,
@ -131,8 +131,8 @@ marketplacesuite "Simulate invalid proofs":
let purchaseId = (
await client0.requestStorage(
cid,
expiry = expiry.stuint(40),
duration = duration.stuint(40),
expiry = expiry,
duration = duration,
nodes = ecNodes,
tolerance = ecTolerance,
proofProbability = 1.u256,
@ -141,7 +141,7 @@ marketplacesuite "Simulate invalid proofs":
let requestId = (await client0.requestId(purchaseId)).get
check eventually(
await client0.purchaseStateIs(purchaseId, "started"), timeout = expiry.int * 1000
await client0.purchaseStateIs(purchaseId, "started"), timeout = expiry.u64.int * 1000
)
var slotWasFreed = false
@ -151,7 +151,7 @@ marketplacesuite "Simulate invalid proofs":
let subscription = await marketplace.subscribe(SlotFreed, onSlotFreed)
check eventually(slotWasFreed, timeout = (duration - expiry).int * 1000)
check eventually(slotWasFreed, timeout = (duration - expiry).u64.int * 1000)
await subscription.unsubscribe()
@ -185,7 +185,7 @@ marketplacesuite "Simulate invalid proofs":
let datasetSize =
datasetSize(blocks = blocks, nodes = ecNodes, tolerance = ecTolerance)
await createAvailabilities(
datasetSize.truncate(uint64),
datasetSize,
duration,
collateralPerByte,
minPricePerBytePerSecond,
@ -195,8 +195,8 @@ marketplacesuite "Simulate invalid proofs":
let purchaseId = await client0.requestStorage(
cid,
expiry = expiry.stuint(40),
duration = duration.stuint(40),
expiry = expiry,
duration = duration,
nodes = ecNodes,
tolerance = ecTolerance,
proofProbability = 1.u256,
@ -214,7 +214,7 @@ marketplacesuite "Simulate invalid proofs":
let filledSubscription = await marketplace.subscribe(SlotFilled, onSlotFilled)
# wait for the first slot to be filled
check eventually(slotWasFilled, timeout = expiry.int * 1000)
check eventually(slotWasFilled, timeout = expiry.u64.int * 1000)
var slotWasFreed = false
proc onSlotFreed(event: ?!SlotFreed) =
@ -224,7 +224,7 @@ marketplacesuite "Simulate invalid proofs":
let freedSubscription = await marketplace.subscribe(SlotFreed, onSlotFreed)
# In 2 periods you cannot have enough invalid proofs submitted:
await sleepAsync(2.periods.int.seconds)
await sleepAsync(2.periods.u64.int.seconds)
check not slotWasFreed
await filledSubscription.unsubscribe()

View File

@ -12,21 +12,21 @@ twonodessuite "Purchasing":
let id1 = (
await client1.requestStorage(
cid,
duration = 100.stuint(40),
pricePerBytePerSecond = 1.stuint(96),
duration = 100'StorageDuration,
pricePerBytePerSecond = 1'TokensPerSecond,
proofProbability = 3.u256,
expiry = 10.stuint(40),
collateralPerByte = 1.u128,
expiry = 10'StorageDuration,
collateralPerByte = 1'Tokens,
)
).get
let id2 = (
await client1.requestStorage(
cid,
duration = 400.stuint(40),
pricePerBytePerSecond = 2.stuint(96),
duration = 400'StorageDuration,
pricePerBytePerSecond = 2'TokensPerSecond,
proofProbability = 6.u256,
expiry = 10.stuint(40),
collateralPerByte = 2.u128,
expiry = 10'StorageDuration,
collateralPerByte = 2'Tokens,
)
).get
check id1 != id2
@ -42,11 +42,11 @@ twonodessuite "Purchasing":
let id = (
await client1.requestStorage(
cid,
duration = 100.stuint(40),
pricePerBytePerSecond = 1.stuint(96),
duration = 100'StorageDuration,
pricePerBytePerSecond = 1'TokensPerSecond,
proofProbability = 3.u256,
expiry = 30.stuint(40),
collateralPerByte = 1.u128,
expiry = 30'StorageDuration,
collateralPerByte = 1'Tokens,
nodes = 3,
tolerance = 1,
)
@ -55,11 +55,11 @@ twonodessuite "Purchasing":
let request = (await client1.getPurchase(id)).get.request.get
check request.content.cid.data.buffer.len > 0
check request.ask.duration == 100.stuint(40)
check request.ask.pricePerBytePerSecond == 1.stuint(96)
check request.ask.duration == 100'StorageDuration
check request.ask.pricePerBytePerSecond == 1'TokensPerSecond
check request.ask.proofProbability == 3.u256
check request.expiry == 30.stuint(40)
check request.ask.collateralPerByte == 1.u128
check request.expiry == 30'StorageDuration
check request.ask.collateralPerByte == 1'Tokens
check request.ask.slots == 3'u64
check request.ask.maxSlotLoss == 1'u64
@ -85,11 +85,11 @@ twonodessuite "Purchasing":
let id = (
await client1.requestStorage(
cid,
duration = (10 * 60).stuint(40),
pricePerBytePerSecond = 1.stuint(96),
duration = StorageDuration.init(10'u32 * 60),
pricePerBytePerSecond = 1'TokensPerSecond,
proofProbability = 3.u256,
expiry = 5 * 60.stuint(40),
collateralPerByte = 1.u128,
expiry = StorageDuration.init(5'u32 * 60),
collateralPerByte = 1'Tokens,
nodes = 3.uint,
tolerance = 1.uint,
)
@ -104,11 +104,11 @@ twonodessuite "Purchasing":
await client1.purchaseStateIs(id, "submitted"), timeout = 3 * 60 * 1000
)
let request = (await client1.getPurchase(id)).get.request.get
check request.ask.duration == (10 * 60).stuint(40)
check request.ask.pricePerBytePerSecond == 1.stuint(96)
check request.ask.duration == StorageDuration.init(10'u32 * 60)
check request.ask.pricePerBytePerSecond == 1'TokensPerSecond
check request.ask.proofProbability == 3.u256
check request.expiry == (5 * 60).stuint(40)
check request.ask.collateralPerByte == 1.u128
check request.expiry == StorageDuration.init(5'u32 * 60)
check request.ask.collateralPerByte == 1'Tokens
check request.ask.slots == 3'u64
check request.ask.maxSlotLoss == 1'u64
@ -118,10 +118,10 @@ twonodessuite "Purchasing":
let responseMissing = await client1.requestStorageRaw(
cid,
duration = 1.stuint(40),
pricePerBytePerSecond = 1.stuint(96),
duration = 1'StorageDuration,
pricePerBytePerSecond = 1'TokensPerSecond,
proofProbability = 3.u256,
collateralPerByte = 1.u128,
collateralPerByte = 1'Tokens,
)
check responseMissing.status == 422
check (await responseMissing.body) ==
@ -129,11 +129,11 @@ twonodessuite "Purchasing":
let responseBefore = await client1.requestStorageRaw(
cid,
duration = 10.stuint(40),
pricePerBytePerSecond = 1.stuint(96),
duration = 10'StorageDuration,
pricePerBytePerSecond = 1'TokensPerSecond,
proofProbability = 3.u256,
collateralPerByte = 1.u128,
expiry = 10.stuint(40),
collateralPerByte = 1'Tokens,
expiry = 10'StorageDuration,
)
check responseBefore.status == 422
check "Expiry must be greater than zero and less than the request's duration" in

View File

@ -27,15 +27,12 @@ twonodessuite "REST API":
test "node shows used and available space", twoNodesConfig:
discard (await client1.upload("some file contents")).get
let totalSize = 12.uint64
let minPricePerBytePerSecond = 1.u256
let totalCollateral = totalSize.u256 * minPricePerBytePerSecond
discard (
await client1.postAvailability(
totalSize = totalSize,
duration = 2.uint64,
minPricePerBytePerSecond = minPricePerBytePerSecond,
totalCollateral = totalCollateral,
totalSize = 12.uint64,
duration = 2'StorageDuration,
minPricePerBytePerSecond = 1'TokensPerSecond,
totalCollateral = 100'Tokens,
enabled = true.some,
)
).get
@ -63,11 +60,11 @@ twonodessuite "REST API":
let response = (
await client1.requestStorageRaw(
cid,
duration = 10.stuint(40),
pricePerBytePerSecond = 1.stuint(96),
duration = 10'StorageDuration,
pricePerBytePerSecond = 1'TokensPerSecond,
proofProbability = 3.u256,
collateralPerByte = 1.u128,
expiry = 9.stuint(40),
collateralPerByte = 1'Tokens,
expiry = 9'StorageDuration,
)
)
@ -84,11 +81,11 @@ twonodessuite "REST API":
let cid = (await client1.upload(data)).get
let response = await client1.requestStorageRaw(
cid,
duration = 100.stuint(40),
pricePerBytePerSecond = 1.stuint(96),
duration = 100'StorageDuration,
pricePerBytePerSecond = 1'TokensPerSecond,
proofProbability = 3.u256,
collateralPerByte = 1.u128,
expiry = 30.stuint(40),
collateralPerByte = 1'Tokens,
expiry = 30'StorageDuration,
nodes = nodes.uint,
tolerance = tolerance.uint,
)

View File

@ -23,11 +23,11 @@ multinodesuite "Rest API validation":
let response = await client.requestStorageRaw(
cid,
duration = 100.stuint(40),
pricePerBytePerSecond = 1.stuint(96),
duration = 100'StorageDuration,
pricePerBytePerSecond = 1'TokensPerSecond,
proofProbability = 3.u256,
collateralPerByte = 1.u128,
expiry = 30.stuint(40),
collateralPerByte = 1'Tokens,
expiry = 30'StorageDuration,
nodes = 3.uint,
tolerance = 0.uint,
)
@ -39,11 +39,11 @@ multinodesuite "Rest API validation":
let cid = (await client.upload("some file contents")).get
let response = await client.requestStorageRaw(
cid,
duration = 10.stuint(40),
pricePerBytePerSecond = 1.stuint(96),
duration = 10'StorageDuration,
pricePerBytePerSecond = 1'TokensPerSecond,
proofProbability = 3.u256,
collateralPerByte = 1.u128,
expiry = 9.stuint(40),
collateralPerByte = 1'Tokens,
expiry = 9'StorageDuration,
)
check:
@ -63,11 +63,11 @@ multinodesuite "Rest API validation":
let response = (
await client.requestStorageRaw(
cid,
duration = 100.stuint(40),
pricePerBytePerSecond = 1.stuint(96),
duration = 100'StorageDuration,
pricePerBytePerSecond = 1'TokensPerSecond,
proofProbability = 3.u256,
expiry = 30.stuint(40),
collateralPerByte = 1.u128,
expiry = 30'StorageDuration,
collateralPerByte = 1'Tokens,
nodes = nodes.uint,
tolerance =tolerance.uint,
)
@ -84,11 +84,11 @@ multinodesuite "Rest API validation":
let response = (
await client.requestStorageRaw(
cid,
duration = 100.stuint(40),
pricePerBytePerSecond = 1.stuint(96),
duration = 100'StorageDuration,
pricePerBytePerSecond = 1'TokensPerSecond,
proofProbability = 3.u256,
expiry = 30.stuint(40),
collateralPerByte = 1.u128,
expiry = 30'StorageDuration,
collateralPerByte = 1'Tokens,
nodes = 3.uint,
tolerance = 0.uint,
)
@ -114,9 +114,9 @@ multinodesuite "Rest API validation":
test "updating non-existing availability", config:
let nonExistingResponse = await client.patchAvailabilityRaw(
AvailabilityId.example,
duration = 100.uint64.some,
minPricePerBytePerSecond = 2.u256.some,
totalCollateral = 200.u256.some,
duration = some 100'StorageDuration,
minPricePerBytePerSecond = some 2'TokensPerSecond,
totalCollateral = some 200'Tokens,
)
check nonExistingResponse.status == 404
@ -124,9 +124,9 @@ multinodesuite "Rest API validation":
let availability = (
await client.postAvailability(
totalSize = 140000.uint64,
duration = 200.uint64,
minPricePerBytePerSecond = 3.u256,
totalCollateral = 300.u256,
duration = 200'StorageDuration,
minPricePerBytePerSecond = 3'TokensPerSecond,
totalCollateral = 300'Tokens,
)
).get
let freeSizeResponse =
@ -137,9 +137,9 @@ multinodesuite "Rest API validation":
test "creating availability above the node quota returns 422", config:
let response = await client.postAvailabilityRaw(
totalSize = 24000000000.uint64,
duration = 200.uint64,
minPricePerBytePerSecond = 3.u256,
totalCollateral = 300.u256,
duration = 200'StorageDuration,
minPricePerBytePerSecond = 3'TokensPerSecond,
totalCollateral = 300'Tokens,
)
check response.status == 422
@ -149,9 +149,9 @@ multinodesuite "Rest API validation":
let availability = (
await client.postAvailability(
totalSize = 140000.uint64,
duration = 200.uint64,
minPricePerBytePerSecond = 3.u256,
totalCollateral = 300.u256,
duration = 200'StorageDuration,
minPricePerBytePerSecond = 3'TokensPerSecond,
totalCollateral = 300'Tokens,
)
).get
let response = await client.patchAvailabilityRaw(
@ -164,9 +164,9 @@ multinodesuite "Rest API validation":
test "creating availability when total size is zero returns 422", config:
let response = await client.postAvailabilityRaw(
totalSize = 0.uint64,
duration = 200.uint64,
minPricePerBytePerSecond = 3.u256,
totalCollateral = 300.u256,
duration = 200'StorageDuration,
minPricePerBytePerSecond = 3'TokensPerSecond,
totalCollateral = 300'Tokens,
)
check response.status == 422
@ -176,9 +176,9 @@ multinodesuite "Rest API validation":
let availability = (
await client.postAvailability(
totalSize = 140000.uint64,
duration = 200.uint64,
minPricePerBytePerSecond = 3.u256,
totalCollateral = 300.u256,
duration = 200'StorageDuration,
minPricePerBytePerSecond = 3'TokensPerSecond,
totalCollateral = 300'Tokens,
)
).get
let response =
@ -204,9 +204,9 @@ multinodesuite "Rest API validation":
let availability = (
await client.postAvailability(
totalSize = 140000.uint64,
duration = 200.uint64,
minPricePerBytePerSecond = 3.u256,
totalCollateral = 300.u256,
duration = 200'StorageDuration,
minPricePerBytePerSecond = 3'TokensPerSecond,
totalCollateral = 300'Tokens,
)
).get
@ -223,11 +223,11 @@ multinodesuite "Rest API validation":
let cid = (await client.upload(data)).get
let response = await client.requestStorageRaw(
cid,
duration = 100.stuint(40),
pricePerBytePerSecond = 1.stuint(96),
duration = 100'StorageDuration,
pricePerBytePerSecond = 1'TokensPerSecond,
proofProbability = 3.u256,
expiry = 30.stuint(40),
collateralPerByte = 1.u128,
expiry = 30'StorageDuration,
collateralPerByte = 1'Tokens,
nodes = 3.uint,
tolerance = 0.uint,
)
@ -241,11 +241,11 @@ multinodesuite "Rest API validation":
let response = await client.requestStorageRaw(
cid,
duration = (31 * 24 * 60 * 60).stuint(40), # 31 days TODO: this should not be hardcoded, but waits for https://github.com/codex-storage/nim-codex/issues/1056
pricePerBytePerSecond = 1.stuint(96),
duration = StorageDuration.init(31'u32 * 24 * 60 * 60), # 31 days TODO: this should not be hardcoded, but waits for https://github.com/codex-storage/nim-codex/issues/1056
pricePerBytePerSecond = 1'TokensPerSecond,
proofProbability = 3.u256,
expiry = 30.stuint(40),
collateralPerByte = 1.u128,
expiry = 30'StorageDuration,
collateralPerByte = 1'Tokens,
nodes = 3.uint,
tolerance = 2.uint,
)
@ -259,11 +259,11 @@ multinodesuite "Rest API validation":
let response = await client.requestStorageRaw(
cid,
duration = 100.stuint(40),
pricePerBytePerSecond = 1.stuint(96),
duration = 100'StorageDuration,
pricePerBytePerSecond = 1'TokensPerSecond,
proofProbability = 3.u256,
expiry = 0.stuint(40),
collateralPerByte = 1.u128,
expiry = 0'StorageDuration,
collateralPerByte = 1'Tokens,
nodes = 3.uint,
tolerance = 0.uint,
)
@ -278,11 +278,11 @@ multinodesuite "Rest API validation":
let response = await client.requestStorageRaw(
cid,
duration = 100.stuint(40),
pricePerBytePerSecond = 1.stuint(96),
duration = 100'StorageDuration,
pricePerBytePerSecond = 1'TokensPerSecond,
proofProbability = 0.u256,
expiry = 30.stuint(40),
collateralPerByte = 1.u128,
expiry = 30'StorageDuration,
collateralPerByte = 1'Tokens,
nodes = 3.uint,
tolerance = 0.uint,
)
@ -296,11 +296,11 @@ multinodesuite "Rest API validation":
let response = await client.requestStorageRaw(
cid,
duration = 100.stuint(40),
pricePerBytePerSecond = 0.stuint(96),
duration = 100'StorageDuration,
pricePerBytePerSecond = 0'TokensPerSecond,
proofProbability = 3.u256,
expiry = 30.stuint(40),
collateralPerByte = 1.u128,
expiry = 30'StorageDuration,
collateralPerByte = 1'Tokens,
nodes = 3.uint,
tolerance = 0.uint,
)
@ -315,30 +315,14 @@ multinodesuite "Rest API validation":
let response = await client.requestStorageRaw(
cid,
duration = 100.stuint(40),
pricePerBytePerSecond = 1.stuint(96),
duration = 100'StorageDuration,
pricePerBytePerSecond = 1'TokensPerSecond,
proofProbability = 3.u256,
expiry = 30.stuint(40),
collateralPerByte = 0.u128,
expiry = 30'StorageDuration,
collateralPerByte = 0'Tokens,
nodes = 3.uint,
tolerance = 0.uint,
)
check response.status == 422
check (await response.body) == "Collateral per byte must be greater than zero"
test "creating availability fails when until is negative", config:
let totalSize = 12.uint64
let minPricePerBytePerSecond = 1.u256
let totalCollateral = totalSize.u256 * minPricePerBytePerSecond
let response = await client.postAvailabilityRaw(
totalSize = totalSize,
duration = 2.uint64,
minPricePerBytePerSecond = minPricePerBytePerSecond,
totalCollateral = totalCollateral,
until = -1.SecondsSince1970.some,
)
check:
response.status == 422
(await response.body) == "Cannot set until to a negative value"

View File

@ -36,17 +36,17 @@ multinodesuite "Sales":
let availability1 = (
await host.postAvailability(
totalSize = 1.uint64,
duration = 2.uint64,
minPricePerBytePerSecond = 3.u256,
totalCollateral = 4.u256,
duration = 2'StorageDuration,
minPricePerBytePerSecond = 3'TokensPerSecond,
totalCollateral = 4'Tokens,
)
).get
let availability2 = (
await host.postAvailability(
totalSize = 4.uint64,
duration = 5.uint64,
minPricePerBytePerSecond = 6.u256,
totalCollateral = 7.u256,
duration = 5'StorageDuration,
minPricePerBytePerSecond = 6'TokensPerSecond,
totalCollateral = 7'Tokens,
)
).get
check availability1 != availability2
@ -55,9 +55,9 @@ multinodesuite "Sales":
let availability = (
await host.postAvailability(
totalSize = 1.uint64,
duration = 2.uint64,
minPricePerBytePerSecond = 3.u256,
totalCollateral = 4.u256,
duration = 2'StorageDuration,
minPricePerBytePerSecond = 3'TokensPerSecond,
totalCollateral = 4'Tokens,
)
).get
check availability in (await host.getAvailabilities()).get
@ -66,28 +66,28 @@ multinodesuite "Sales":
let availability = (
await host.postAvailability(
totalSize = 140000.uint64,
duration = 200.uint64,
minPricePerBytePerSecond = 3.u256,
totalCollateral = 300.u256,
duration = 200'StorageDuration,
minPricePerBytePerSecond = 3'TokensPerSecond,
totalCollateral = 300'Tokens,
)
).get
var until = getTime().toUnix()
var until = StorageTimestamp.init(getTime().toUnix())
await host.patchAvailability(
availability.id,
duration = 100.uint64.some,
minPricePerBytePerSecond = 2.u256.some,
totalCollateral = 200.u256.some,
duration = some 100'StorageDuration,
minPricePerBytePerSecond = some 2'TokensPerSecond,
totalCollateral = some 200'Tokens,
enabled = false.some,
until = until.some,
)
let updatedAvailability =
((await host.getAvailabilities()).get).findItem(availability).get
check updatedAvailability.duration == 100.uint64
check updatedAvailability.minPricePerBytePerSecond == 2
check updatedAvailability.totalCollateral == 200
check updatedAvailability.duration == 100'StorageDuration
check updatedAvailability.minPricePerBytePerSecond == 2'TokensPerSecond
check updatedAvailability.totalCollateral == 200'Tokens
check updatedAvailability.totalSize == 140000.uint64
check updatedAvailability.freeSize == 140000.uint64
check updatedAvailability.enabled == false
@ -97,9 +97,9 @@ multinodesuite "Sales":
let availability = (
await host.postAvailability(
totalSize = 140000.uint64,
duration = 200.uint64,
minPricePerBytePerSecond = 3.u256,
totalCollateral = 300.u256,
duration = 200'StorageDuration,
minPricePerBytePerSecond = 3'TokensPerSecond,
totalCollateral = 300'Tokens,
)
).get
await host.patchAvailability(availability.id, totalSize = 100000.uint64.some)
@ -113,13 +113,13 @@ multinodesuite "Sales":
salesConfig:
let originalSize = 0xFFFFFF.uint64
let data = await RandomChunker.example(blocks = 8)
let minPricePerBytePerSecond = 3.u256
let collateralPerByte = 1.u256
let totalCollateral = originalSize.u256 * collateralPerByte
let minPricePerBytePerSecond = 3'TokensPerSecond
let collateralPerByte = 1'Tokens
let totalCollateral = collateralPerByte * originalSize
let availability = (
await host.postAvailability(
totalSize = originalSize,
duration = 20 * 60.uint64,
duration = StorageDuration.init(20'u32 * 60),
minPricePerBytePerSecond = minPricePerBytePerSecond,
totalCollateral = totalCollateral,
)
@ -130,11 +130,11 @@ multinodesuite "Sales":
let id = (
await client.requestStorage(
cid,
duration = (20 * 60).stuint(40),
pricePerBytePerSecond = minPricePerBytePerSecond.stuint(96),
duration = StorageDuration.init(20'u32 * 60),
pricePerBytePerSecond = minPricePerBytePerSecond,
proofProbability = 3.u256,
expiry = (10 * 60).stuint(40),
collateralPerByte = collateralPerByte.stuint(128),
expiry = StorageDuration.init(10'u32 * 60),
collateralPerByte = collateralPerByte,
nodes = 3,
tolerance = 1,
)
@ -165,29 +165,13 @@ multinodesuite "Sales":
check newUpdatedAvailability.totalSize == originalSize + 20000
check newUpdatedAvailability.freeSize - updatedAvailability.freeSize == 20000
test "updating availability fails with until negative", salesConfig:
let availability = (
await host.postAvailability(
totalSize = 140000.uint64,
duration = 200.uint64,
minPricePerBytePerSecond = 3.u256,
totalCollateral = 300.u256,
)
).get
let response =
await host.patchAvailabilityRaw(availability.id, until = -1.SecondsSince1970.some)
check:
(await response.body) == "Cannot set until to a negative value"
test "returns an error when trying to update the until date before an existing a request is finished",
salesConfig:
let size = 0xFFFFFF.uint64
let data = await RandomChunker.example(blocks = 8)
let duration = 20 * 60.uint64
let minPricePerBytePerSecond = 3.u256
let collateralPerByte = 1.u256
let duration = StorageDuration.init(20'u32 * 60)
let minPricePerBytePerSecond = 3'TokensPerSecond
let collateralPerByte = 1'Tokens
let ecNodes = 3.uint
let ecTolerance = 1.uint
@ -197,7 +181,7 @@ multinodesuite "Sales":
totalSize = size,
duration = duration,
minPricePerBytePerSecond = minPricePerBytePerSecond,
totalCollateral = size.u256 * minPricePerBytePerSecond,
totalCollateral = collateralPerByte * size,
)
).get
@ -209,7 +193,7 @@ multinodesuite "Sales":
duration = duration,
pricePerBytePerSecond = minPricePerBytePerSecond,
proofProbability = 3.u256,
expiry = 10 * 60.uint64,
expiry = StorageDuration.init(10'u32 * 60),
collateralPerByte = collateralPerByte,
nodes = ecNodes,
tolerance = ecTolerance,
@ -223,7 +207,7 @@ multinodesuite "Sales":
check purchase.error == none string
let unixNow = getTime().toUnix()
let until = unixNow + 1.SecondsSince1970
let until = StorageTimestamp.init(unixNow + 1)
let response = await host.patchAvailabilityRaw(
availabilityId = availability.id, until = until.some

View File

@ -39,8 +39,8 @@ marketplacesuite "Validation":
const ecTolerance = 1
const proofProbability = 1.u256
const collateralPerByte = 1.u256
const minPricePerBytePerSecond = 1.u256
const collateralPerByte = 1'Tokens
const minPricePerBytePerSecond = 1'TokensPerSecond
proc waitForRequestToFail(
marketplace: Marketplace, requestId: RequestId, timeout = 10, step = 5
@ -94,13 +94,13 @@ marketplacesuite "Validation":
discard await ethProvider.send("evm_mine")
var currentTime = await ethProvider.currentTime()
let requestEndTime = currentTime.truncate(uint64) + duration
let requestEndTime = currentTime.truncate(uint64) + duration.u64
let data = await RandomChunker.example(blocks = blocks)
let datasetSize =
datasetSize(blocks = blocks, nodes = ecNodes, tolerance = ecTolerance)
await createAvailabilities(
datasetSize.truncate(uint64),
datasetSize,
duration,
collateralPerByte,
minPricePerBytePerSecond,
@ -109,8 +109,8 @@ marketplacesuite "Validation":
let cid = (await client0.upload(data)).get
let purchaseId = await client0.requestStorage(
cid,
expiry = expiry.stuint(40),
duration = duration.stuint(40),
expiry = expiry,
duration = duration,
nodes = ecNodes,
tolerance = ecTolerance,
proofProbability = proofProbability,
@ -121,7 +121,7 @@ marketplacesuite "Validation":
if not eventuallyS(
await client0.purchaseStateIs(purchaseId, "started"),
timeout = (expiry + 60).int,
timeout = (expiry.u64 + 60).int,
step = 5,
):
debug "validation suite: timed out waiting for the purchase to start"
@ -164,13 +164,13 @@ marketplacesuite "Validation":
discard await ethProvider.send("evm_mine")
var currentTime = await ethProvider.currentTime()
let requestEndTime = currentTime.truncate(uint64) + duration
let requestEndTime = currentTime.truncate(uint64) + duration.u64
let data = await RandomChunker.example(blocks = blocks)
let datasetSize =
datasetSize(blocks = blocks, nodes = ecNodes, tolerance = ecTolerance)
await createAvailabilities(
datasetSize.truncate(uint64),
datasetSize,
duration,
collateralPerByte,
minPricePerBytePerSecond,
@ -179,8 +179,8 @@ marketplacesuite "Validation":
let cid = (await client0.upload(data)).get
let purchaseId = await client0.requestStorage(
cid,
expiry = expiry.stuint(40),
duration = duration.stuint(40),
expiry = expiry,
duration = duration,
nodes = ecNodes,
tolerance = ecTolerance,
proofProbability = proofProbability,
@ -191,7 +191,7 @@ marketplacesuite "Validation":
if not eventuallyS(
await client0.purchaseStateIs(purchaseId, "started"),
timeout = (expiry + 60).int,
timeout = (expiry.u64 + 60).int,
step = 5,
):
debug "validation suite: timed out waiting for the purchase to start"