refactor: merging proving module into sales (#469)
Co-authored-by: Eric <5089238+emizzle@users.noreply.github.com>
This commit is contained in:
parent
9cecb68520
commit
f459a2c6f6
|
@ -55,9 +55,9 @@ proc bootstrapInteractions(
|
||||||
config: CodexConf,
|
config: CodexConf,
|
||||||
repo: RepoStore
|
repo: RepoStore
|
||||||
): Future[Contracts] {.async.} =
|
): Future[Contracts] {.async.} =
|
||||||
## bootstrap interactions and return contracts
|
## bootstrap interactions and return contracts
|
||||||
## using clients, hosts, validators pairings
|
## using clients, hosts, validators pairings
|
||||||
##
|
##
|
||||||
|
|
||||||
if not config.persistence and not config.validator:
|
if not config.persistence and not config.validator:
|
||||||
if config.ethAccount.isSome:
|
if config.ethAccount.isSome:
|
||||||
|
@ -86,18 +86,23 @@ proc bootstrapInteractions(
|
||||||
var client: ?ClientInteractions
|
var client: ?ClientInteractions
|
||||||
var host: ?HostInteractions
|
var host: ?HostInteractions
|
||||||
var validator: ?ValidatorInteractions
|
var validator: ?ValidatorInteractions
|
||||||
|
|
||||||
if config.persistence:
|
if config.persistence:
|
||||||
let purchasing = Purchasing.new(market, clock)
|
# This is used for simulation purposes. Normal nodes won't be compiled with this flag
|
||||||
|
# and hence the proof failure will always be 0.
|
||||||
when codex_enable_proof_failures:
|
when codex_enable_proof_failures:
|
||||||
let proving = if config.simulateProofFailures > 0:
|
let proofFailures = config.simulateProofFailures
|
||||||
SimulatedProving.new(market, clock,
|
if proofFailures > 0:
|
||||||
config.simulateProofFailures)
|
warn "Enabling proof failure simulation!"
|
||||||
else: Proving.new(market, clock)
|
|
||||||
else:
|
else:
|
||||||
let proving = Proving.new(market, clock)
|
let proofFailures = 0
|
||||||
let sales = Sales.new(market, clock, proving, repo)
|
if config.simulateProofFailures > 0:
|
||||||
|
warn "Proof failure simulation is not enabled for this build! Configuration ignored"
|
||||||
|
|
||||||
|
let purchasing = Purchasing.new(market, clock)
|
||||||
|
let sales = Sales.new(market, clock, repo, proofFailures)
|
||||||
client = some ClientInteractions.new(clock, purchasing)
|
client = some ClientInteractions.new(clock, purchasing)
|
||||||
host = some HostInteractions.new(clock, sales, proving)
|
host = some HostInteractions.new(clock, sales)
|
||||||
if config.validator:
|
if config.validator:
|
||||||
let validation = Validation.new(clock, market, config.validatorMaxSlots)
|
let validation = Validation.new(clock, market, config.validatorMaxSlots)
|
||||||
validator = some ValidatorInteractions.new(clock, validation)
|
validator = some ValidatorInteractions.new(clock, validation)
|
||||||
|
|
|
@ -251,7 +251,7 @@ type
|
||||||
defaultValue: 0
|
defaultValue: 0
|
||||||
name: "simulate-proof-failures"
|
name: "simulate-proof-failures"
|
||||||
hidden
|
hidden
|
||||||
.}: uint
|
.}: int
|
||||||
|
|
||||||
of initNode:
|
of initNode:
|
||||||
discard
|
discard
|
||||||
|
|
|
@ -2,34 +2,28 @@ import pkg/ethers
|
||||||
import pkg/chronicles
|
import pkg/chronicles
|
||||||
|
|
||||||
import ../../sales
|
import ../../sales
|
||||||
import ../../proving
|
|
||||||
import ./interactions
|
import ./interactions
|
||||||
|
|
||||||
export sales
|
export sales
|
||||||
export proving
|
|
||||||
export chronicles
|
export chronicles
|
||||||
|
|
||||||
type
|
type
|
||||||
HostInteractions* = ref object of ContractInteractions
|
HostInteractions* = ref object of ContractInteractions
|
||||||
sales*: Sales
|
sales*: Sales
|
||||||
proving*: Proving
|
|
||||||
|
|
||||||
proc new*(
|
proc new*(
|
||||||
_: type HostInteractions,
|
_: type HostInteractions,
|
||||||
clock: OnChainClock,
|
clock: OnChainClock,
|
||||||
sales: Sales,
|
sales: Sales
|
||||||
proving: Proving
|
|
||||||
): HostInteractions =
|
): HostInteractions =
|
||||||
## Create a new HostInteractions instance
|
## Create a new HostInteractions instance
|
||||||
##
|
##
|
||||||
HostInteractions(clock: clock, sales: sales, proving: proving)
|
HostInteractions(clock: clock, sales: sales)
|
||||||
|
|
||||||
method start*(self: HostInteractions) {.async.} =
|
method start*(self: HostInteractions) {.async.} =
|
||||||
await procCall ContractInteractions(self).start()
|
await procCall ContractInteractions(self).start()
|
||||||
await self.sales.start()
|
await self.sales.start()
|
||||||
await self.proving.start()
|
|
||||||
|
|
||||||
method stop*(self: HostInteractions) {.async.} =
|
method stop*(self: HostInteractions) {.async.} =
|
||||||
await self.sales.stop()
|
await self.sales.stop()
|
||||||
await self.proving.stop()
|
|
||||||
await procCall ContractInteractions(self).start()
|
await procCall ContractInteractions(self).start()
|
||||||
|
|
|
@ -372,7 +372,7 @@ proc start*(node: CodexNodeRef) {.async.} =
|
||||||
# TODO: remove data from local storage
|
# TODO: remove data from local storage
|
||||||
discard
|
discard
|
||||||
|
|
||||||
hostContracts.proving.onProve = proc(slot: Slot): Future[seq[byte]] {.async.} =
|
hostContracts.sales.onProve = proc(slot: Slot): Future[seq[byte]] {.async.} =
|
||||||
# TODO: generate proof
|
# TODO: generate proof
|
||||||
return @[42'u8]
|
return @[42'u8]
|
||||||
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
import ./proving/proving
|
|
||||||
import ./proving/simulated
|
|
||||||
|
|
||||||
export proving
|
|
||||||
export simulated
|
|
|
@ -1,101 +0,0 @@
|
||||||
import std/sets
|
|
||||||
import pkg/upraises
|
|
||||||
import pkg/questionable
|
|
||||||
import pkg/chronicles
|
|
||||||
import ../market
|
|
||||||
import ../clock
|
|
||||||
|
|
||||||
export sets
|
|
||||||
|
|
||||||
logScope:
|
|
||||||
topics = "marketplace proving"
|
|
||||||
|
|
||||||
type
|
|
||||||
Proving* = ref object of RootObj
|
|
||||||
market*: Market
|
|
||||||
clock: Clock
|
|
||||||
loop: ?Future[void]
|
|
||||||
slots*: HashSet[Slot]
|
|
||||||
onProve: ?OnProve
|
|
||||||
OnProve* = proc(slot: Slot): Future[seq[byte]] {.gcsafe, upraises: [].}
|
|
||||||
|
|
||||||
func new*(T: type Proving, market: Market, clock: Clock): T =
|
|
||||||
T(market: market, clock: clock)
|
|
||||||
|
|
||||||
proc onProve*(proving: Proving): ?OnProve =
|
|
||||||
proving.onProve
|
|
||||||
|
|
||||||
proc `onProve=`*(proving: Proving, callback: OnProve) =
|
|
||||||
proving.onProve = some callback
|
|
||||||
|
|
||||||
func add*(proving: Proving, slot: Slot) =
|
|
||||||
proving.slots.incl(slot)
|
|
||||||
|
|
||||||
proc getCurrentPeriod*(proving: Proving): Future[Period] {.async.} =
|
|
||||||
let periodicity = await proving.market.periodicity()
|
|
||||||
return periodicity.periodOf(proving.clock.now().u256)
|
|
||||||
|
|
||||||
proc waitUntilPeriod(proving: Proving, period: Period) {.async.} =
|
|
||||||
let periodicity = await proving.market.periodicity()
|
|
||||||
await proving.clock.waitUntil(periodicity.periodStart(period).truncate(int64))
|
|
||||||
|
|
||||||
proc removeEndedContracts(proving: Proving) {.async.} =
|
|
||||||
var ended: HashSet[Slot]
|
|
||||||
for slot in proving.slots:
|
|
||||||
let state = await proving.market.slotState(slot.id)
|
|
||||||
if state == SlotState.Finished:
|
|
||||||
debug "Collecting finished slot's reward", slot = $slot.id
|
|
||||||
await proving.market.freeSlot(slot.id)
|
|
||||||
|
|
||||||
if state != SlotState.Filled:
|
|
||||||
debug "Request ended, cleaning up slot", slot = $slot.id
|
|
||||||
ended.incl(slot)
|
|
||||||
proving.slots.excl(ended)
|
|
||||||
|
|
||||||
method prove*(proving: Proving, slot: Slot) {.base, async.} =
|
|
||||||
logScope:
|
|
||||||
currentPeriod = await proving.getCurrentPeriod()
|
|
||||||
|
|
||||||
without onProve =? proving.onProve:
|
|
||||||
raiseAssert "onProve callback not set"
|
|
||||||
try:
|
|
||||||
debug "Proving slot"
|
|
||||||
let proof = await onProve(slot)
|
|
||||||
trace "submitting proof", currentPeriod = await proving.getCurrentPeriod()
|
|
||||||
await proving.market.submitProof(slot.id, proof)
|
|
||||||
except CatchableError as e:
|
|
||||||
error "Submitting proof failed", msg = e.msg
|
|
||||||
|
|
||||||
proc run(proving: Proving) {.async.} =
|
|
||||||
try:
|
|
||||||
while true:
|
|
||||||
let currentPeriod = await proving.getCurrentPeriod()
|
|
||||||
debug "Proving for new period", period = currentPeriod
|
|
||||||
await proving.removeEndedContracts()
|
|
||||||
for slot in proving.slots:
|
|
||||||
let id = slot.id
|
|
||||||
if (await proving.market.isProofRequired(id)) or
|
|
||||||
(await proving.market.willProofBeRequired(id)):
|
|
||||||
asyncSpawn proving.prove(slot)
|
|
||||||
await proving.waitUntilPeriod(currentPeriod + 1)
|
|
||||||
except CancelledError:
|
|
||||||
discard
|
|
||||||
except CatchableError as e:
|
|
||||||
error "Proving failed", msg = e.msg
|
|
||||||
|
|
||||||
proc start*(proving: Proving) {.async.} =
|
|
||||||
if proving.loop.isSome:
|
|
||||||
return
|
|
||||||
|
|
||||||
proving.loop = some proving.run()
|
|
||||||
|
|
||||||
proc stop*(proving: Proving) {.async.} =
|
|
||||||
if loop =? proving.loop:
|
|
||||||
proving.loop = Future[void].none
|
|
||||||
if not loop.finished:
|
|
||||||
await loop.cancelAndWait()
|
|
||||||
|
|
||||||
proc subscribeProofSubmission*(proving: Proving,
|
|
||||||
callback: OnProofSubmitted):
|
|
||||||
Future[Subscription] =
|
|
||||||
proving.market.subscribeProofSubmission(callback)
|
|
|
@ -1,47 +0,0 @@
|
||||||
import ../conf
|
|
||||||
when codex_enable_proof_failures:
|
|
||||||
|
|
||||||
import std/strutils
|
|
||||||
import pkg/chronicles
|
|
||||||
import pkg/ethers
|
|
||||||
import pkg/ethers/testing
|
|
||||||
import ../market
|
|
||||||
import ../clock
|
|
||||||
import ./proving
|
|
||||||
|
|
||||||
type
|
|
||||||
SimulatedProving* = ref object of Proving
|
|
||||||
failEveryNProofs: uint
|
|
||||||
proofCount: uint
|
|
||||||
|
|
||||||
logScope:
|
|
||||||
topics = "simulated proving"
|
|
||||||
|
|
||||||
func new*(_: type SimulatedProving,
|
|
||||||
market: Market,
|
|
||||||
clock: Clock,
|
|
||||||
failEveryNProofs: uint): SimulatedProving =
|
|
||||||
|
|
||||||
let p = SimulatedProving.new(market, clock)
|
|
||||||
p.failEveryNProofs = failEveryNProofs
|
|
||||||
return p
|
|
||||||
|
|
||||||
proc onSubmitProofError(error: ref CatchableError, period: UInt256) =
|
|
||||||
error "Submitting invalid proof failed", period, msg = error.msg
|
|
||||||
|
|
||||||
method prove(proving: SimulatedProving, slot: Slot) {.async.} =
|
|
||||||
let period = await proving.getCurrentPeriod()
|
|
||||||
proving.proofCount += 1
|
|
||||||
if proving.failEveryNProofs > 0'u and
|
|
||||||
proving.proofCount mod proving.failEveryNProofs == 0'u:
|
|
||||||
proving.proofCount = 0
|
|
||||||
try:
|
|
||||||
trace "submitting INVALID proof", currentPeriod = await proving.getCurrentPeriod()
|
|
||||||
await proving.market.submitProof(slot.id, newSeq[byte](0))
|
|
||||||
except ProviderError as e:
|
|
||||||
if not e.revertReason.contains("Invalid proof"):
|
|
||||||
onSubmitProofError(e, period)
|
|
||||||
except CatchableError as e:
|
|
||||||
onSubmitProofError(e, period)
|
|
||||||
else:
|
|
||||||
await procCall Proving(proving).prove(slot)
|
|
|
@ -7,7 +7,6 @@ import pkg/chronicles
|
||||||
import pkg/datastore
|
import pkg/datastore
|
||||||
import ./market
|
import ./market
|
||||||
import ./clock
|
import ./clock
|
||||||
import ./proving
|
|
||||||
import ./stores
|
import ./stores
|
||||||
import ./contracts/requests
|
import ./contracts/requests
|
||||||
import ./contracts/marketplace
|
import ./contracts/marketplace
|
||||||
|
@ -61,26 +60,37 @@ proc `onClear=`*(sales: Sales, onClear: OnClear) =
|
||||||
proc `onSale=`*(sales: Sales, callback: OnSale) =
|
proc `onSale=`*(sales: Sales, callback: OnSale) =
|
||||||
sales.context.onSale = some callback
|
sales.context.onSale = some callback
|
||||||
|
|
||||||
|
proc `onProve=`*(sales: Sales, callback: OnProve) =
|
||||||
|
sales.context.onProve = some callback
|
||||||
|
|
||||||
proc onStore*(sales: Sales): ?OnStore = sales.context.onStore
|
proc onStore*(sales: Sales): ?OnStore = sales.context.onStore
|
||||||
|
|
||||||
proc onClear*(sales: Sales): ?OnClear = sales.context.onClear
|
proc onClear*(sales: Sales): ?OnClear = sales.context.onClear
|
||||||
|
|
||||||
proc onSale*(sales: Sales): ?OnSale = sales.context.onSale
|
proc onSale*(sales: Sales): ?OnSale = sales.context.onSale
|
||||||
|
|
||||||
|
proc onProve*(sales: Sales): ?OnProve = sales.context.onProve
|
||||||
|
|
||||||
func new*(_: type Sales,
|
func new*(_: type Sales,
|
||||||
market: Market,
|
market: Market,
|
||||||
clock: Clock,
|
clock: Clock,
|
||||||
proving: Proving,
|
|
||||||
repo: RepoStore): Sales =
|
repo: RepoStore): Sales =
|
||||||
|
Sales.new(market, clock, repo, 0)
|
||||||
|
|
||||||
|
func new*(_: type Sales,
|
||||||
|
market: Market,
|
||||||
|
clock: Clock,
|
||||||
|
repo: RepoStore,
|
||||||
|
simulateProofFailures: int): Sales =
|
||||||
|
|
||||||
let reservations = Reservations.new(repo)
|
let reservations = Reservations.new(repo)
|
||||||
Sales(
|
Sales(
|
||||||
context: SalesContext(
|
context: SalesContext(
|
||||||
market: market,
|
market: market,
|
||||||
clock: clock,
|
clock: clock,
|
||||||
proving: proving,
|
|
||||||
reservations: reservations,
|
reservations: reservations,
|
||||||
slotQueue: SlotQueue.new(reservations)
|
slotQueue: SlotQueue.new(reservations),
|
||||||
|
simulateProofFailures: simulateProofFailures
|
||||||
),
|
),
|
||||||
trackedFutures: TrackedFutures.new(),
|
trackedFutures: TrackedFutures.new(),
|
||||||
subscriptions: @[]
|
subscriptions: @[]
|
||||||
|
@ -392,9 +402,9 @@ proc unsubscribe(sales: Sales) {.async.} =
|
||||||
error "Unable to unsubscribe from subscription", error = e.msg
|
error "Unable to unsubscribe from subscription", error = e.msg
|
||||||
|
|
||||||
proc start*(sales: Sales) {.async.} =
|
proc start*(sales: Sales) {.async.} =
|
||||||
|
await sales.load()
|
||||||
await sales.startSlotQueue()
|
await sales.startSlotQueue()
|
||||||
await sales.subscribe()
|
await sales.subscribe()
|
||||||
await sales.load()
|
|
||||||
|
|
||||||
proc stop*(sales: Sales) {.async.} =
|
proc stop*(sales: Sales) {.async.} =
|
||||||
trace "stopping sales"
|
trace "stopping sales"
|
||||||
|
|
|
@ -4,7 +4,6 @@ import pkg/upraises
|
||||||
import ../node/batch
|
import ../node/batch
|
||||||
import ../market
|
import ../market
|
||||||
import ../clock
|
import ../clock
|
||||||
import ../proving
|
|
||||||
import ./slotqueue
|
import ./slotqueue
|
||||||
import ./reservations
|
import ./reservations
|
||||||
|
|
||||||
|
@ -16,15 +15,15 @@ type
|
||||||
onClear*: ?OnClear
|
onClear*: ?OnClear
|
||||||
onSale*: ?OnSale
|
onSale*: ?OnSale
|
||||||
onCleanUp*: OnCleanUp
|
onCleanUp*: OnCleanUp
|
||||||
proving*: Proving
|
onProve*: ?OnProve
|
||||||
reservations*: Reservations
|
reservations*: Reservations
|
||||||
slotQueue*: SlotQueue
|
slotQueue*: SlotQueue
|
||||||
|
simulateProofFailures*: int
|
||||||
|
|
||||||
OnStore* = proc(request: StorageRequest,
|
OnStore* = proc(request: StorageRequest,
|
||||||
slot: UInt256,
|
slot: UInt256,
|
||||||
onBatch: BatchProc): Future[?!void] {.gcsafe, upraises: [].}
|
onBatch: BatchProc): Future[?!void] {.gcsafe, upraises: [].}
|
||||||
OnProve* = proc(request: StorageRequest,
|
OnProve* = proc(slot: Slot): Future[seq[byte]] {.gcsafe, upraises: [].}
|
||||||
slot: UInt256): Future[seq[byte]] {.gcsafe, upraises: [].}
|
|
||||||
OnClear* = proc(request: StorageRequest,
|
OnClear* = proc(request: StorageRequest,
|
||||||
slotIndex: UInt256) {.gcsafe, upraises: [].}
|
slotIndex: UInt256) {.gcsafe, upraises: [].}
|
||||||
OnSale* = proc(request: StorageRequest,
|
OnSale* = proc(request: StorageRequest,
|
||||||
|
|
|
@ -4,13 +4,11 @@ import ../errors
|
||||||
import ../utils/asyncstatemachine
|
import ../utils/asyncstatemachine
|
||||||
import ../market
|
import ../market
|
||||||
import ../clock
|
import ../clock
|
||||||
import ../proving
|
|
||||||
import ../contracts/requests
|
import ../contracts/requests
|
||||||
|
|
||||||
export market
|
export market
|
||||||
export clock
|
export clock
|
||||||
export asyncstatemachine
|
export asyncstatemachine
|
||||||
export proving
|
|
||||||
|
|
||||||
type
|
type
|
||||||
SaleState* = ref object of State
|
SaleState* = ref object of State
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
|
import pkg/chronicles
|
||||||
import ../statemachine
|
import ../statemachine
|
||||||
import ./errorhandling
|
import ./errorhandling
|
||||||
import ./errored
|
import ./errored
|
||||||
|
|
||||||
|
logScope:
|
||||||
|
topics = "marketplace sales cancelled"
|
||||||
|
|
||||||
type
|
type
|
||||||
SaleCancelled* = ref object of ErrorHandlingState
|
SaleCancelled* = ref object of ErrorHandlingState
|
||||||
SaleCancelledError* = object of CatchableError
|
SaleCancelledError* = object of CatchableError
|
||||||
|
|
|
@ -9,7 +9,7 @@ import ./errorhandling
|
||||||
import ./cancelled
|
import ./cancelled
|
||||||
import ./failed
|
import ./failed
|
||||||
import ./filled
|
import ./filled
|
||||||
import ./proving
|
import ./initialproving
|
||||||
import ./errored
|
import ./errored
|
||||||
|
|
||||||
type
|
type
|
||||||
|
@ -80,4 +80,4 @@ method run*(state: SaleDownloading, machine: Machine): Future[?State] {.async.}
|
||||||
trace "Download complete"
|
trace "Download complete"
|
||||||
|
|
||||||
markUnused(availability.id)
|
markUnused(availability.id)
|
||||||
return some State(SaleProving())
|
return some State(SaleInitialProving())
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
|
import pkg/chronicles
|
||||||
import ../statemachine
|
import ../statemachine
|
||||||
import ./errorhandling
|
import ./errorhandling
|
||||||
import ./errored
|
import ./errored
|
||||||
|
|
||||||
|
logScope:
|
||||||
|
topics = "marketplace sales failed"
|
||||||
|
|
||||||
type
|
type
|
||||||
SaleFailed* = ref object of ErrorHandlingState
|
SaleFailed* = ref object of ErrorHandlingState
|
||||||
SaleFailedError* = object of SaleError
|
SaleFailedError* = object of SaleError
|
||||||
|
|
|
@ -1,11 +1,17 @@
|
||||||
import pkg/questionable
|
import pkg/questionable
|
||||||
|
import pkg/chronicles
|
||||||
|
import ../../conf
|
||||||
import ../statemachine
|
import ../statemachine
|
||||||
import ../salesagent
|
import ../salesagent
|
||||||
import ./errorhandling
|
import ./errorhandling
|
||||||
import ./errored
|
import ./errored
|
||||||
import ./finished
|
|
||||||
import ./cancelled
|
import ./cancelled
|
||||||
import ./failed
|
import ./failed
|
||||||
|
import ./proving
|
||||||
|
import ./provingsimulated
|
||||||
|
|
||||||
|
logScope:
|
||||||
|
topics = "marketplace sales filled"
|
||||||
|
|
||||||
type
|
type
|
||||||
SaleFilled* = ref object of ErrorHandlingState
|
SaleFilled* = ref object of ErrorHandlingState
|
||||||
|
@ -21,7 +27,8 @@ method `$`*(state: SaleFilled): string = "SaleFilled"
|
||||||
|
|
||||||
method run*(state: SaleFilled, machine: Machine): Future[?State] {.async.} =
|
method run*(state: SaleFilled, machine: Machine): Future[?State] {.async.} =
|
||||||
let data = SalesAgent(machine).data
|
let data = SalesAgent(machine).data
|
||||||
let market = SalesAgent(machine).context.market
|
let context = SalesAgent(machine).context
|
||||||
|
let market = context.market
|
||||||
|
|
||||||
without slotIndex =? data.slotIndex:
|
without slotIndex =? data.slotIndex:
|
||||||
raiseAssert("no slot index assigned")
|
raiseAssert("no slot index assigned")
|
||||||
|
@ -29,7 +36,19 @@ method run*(state: SaleFilled, machine: Machine): Future[?State] {.async.} =
|
||||||
let host = await market.getHost(data.requestId, slotIndex)
|
let host = await market.getHost(data.requestId, slotIndex)
|
||||||
let me = await market.getSigner()
|
let me = await market.getSigner()
|
||||||
if host == me.some:
|
if host == me.some:
|
||||||
return some State(SaleFinished())
|
info "Slot succesfully filled", requestId = $data.requestId, slotIndex
|
||||||
|
|
||||||
|
if request =? data.request and slotIndex =? data.slotIndex and
|
||||||
|
onSale =? context.onSale:
|
||||||
|
onSale(request, slotIndex)
|
||||||
|
|
||||||
|
when codex_enable_proof_failures:
|
||||||
|
if context.simulateProofFailures > 0:
|
||||||
|
info "Proving with failure rate", rate = context.simulateProofFailures
|
||||||
|
return some State(SaleProvingSimulated(failEveryNProofs: context.simulateProofFailures))
|
||||||
|
|
||||||
|
return some State(SaleProving())
|
||||||
|
|
||||||
else:
|
else:
|
||||||
let error = newException(HostMismatchError, "Slot filled by other host")
|
let error = newException(HostMismatchError, "Slot filled by other host")
|
||||||
return some State(SaleErrored(error: error))
|
return some State(SaleErrored(error: error))
|
||||||
|
|
|
@ -25,16 +25,13 @@ method run*(state: SaleFinished, machine: Machine): Future[?State] {.async.} =
|
||||||
let data = agent.data
|
let data = agent.data
|
||||||
let context = agent.context
|
let context = agent.context
|
||||||
|
|
||||||
debug "Request succesfully filled", requestId = $data.requestId
|
without request =? data.request:
|
||||||
|
raiseAssert "no sale request"
|
||||||
|
|
||||||
if request =? data.request and
|
without slotIndex =? data.slotIndex:
|
||||||
slotIndex =? data.slotIndex:
|
raiseAssert("no slot index assigned")
|
||||||
let slot = Slot(request: request, slotIndex: slotIndex)
|
|
||||||
debug "Adding slot to proving list", slotId = $slot.id
|
|
||||||
context.proving.add(slot)
|
|
||||||
|
|
||||||
if onSale =? context.onSale:
|
info "Slot finished and paid out", requestId = $data.requestId, slotIndex
|
||||||
onSale(request, slotIndex)
|
|
||||||
|
|
||||||
if onCleanUp =? context.onCleanUp:
|
if onCleanUp =? context.onCleanUp:
|
||||||
await onCleanUp()
|
await onCleanUp()
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
|
import pkg/chronicles
|
||||||
import pkg/chronos
|
import pkg/chronos
|
||||||
import ../statemachine
|
import ../statemachine
|
||||||
import ../salesagent
|
import ../salesagent
|
||||||
import ./errorhandling
|
import ./errorhandling
|
||||||
|
|
||||||
|
logScope:
|
||||||
|
topics = "marketplace sales ignored"
|
||||||
|
|
||||||
type
|
type
|
||||||
SaleIgnored* = ref object of ErrorHandlingState
|
SaleIgnored* = ref object of ErrorHandlingState
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
import pkg/chronicles
|
||||||
|
import ../statemachine
|
||||||
|
import ../salesagent
|
||||||
|
import ./errorhandling
|
||||||
|
import ./filling
|
||||||
|
import ./cancelled
|
||||||
|
import ./failed
|
||||||
|
|
||||||
|
logScope:
|
||||||
|
topics = "marketplace sales initial-proving"
|
||||||
|
|
||||||
|
type
|
||||||
|
SaleInitialProving* = ref object of ErrorHandlingState
|
||||||
|
|
||||||
|
method `$`*(state: SaleInitialProving): string = "SaleInitialProving"
|
||||||
|
|
||||||
|
method onCancelled*(state: SaleInitialProving, request: StorageRequest): ?State =
|
||||||
|
return some State(SaleCancelled())
|
||||||
|
|
||||||
|
method onFailed*(state: SaleInitialProving, request: StorageRequest): ?State =
|
||||||
|
return some State(SaleFailed())
|
||||||
|
|
||||||
|
method run*(state: SaleInitialProving, machine: Machine): Future[?State] {.async.} =
|
||||||
|
let data = SalesAgent(machine).data
|
||||||
|
let context = SalesAgent(machine).context
|
||||||
|
|
||||||
|
without request =? data.request:
|
||||||
|
raiseAssert "no sale request"
|
||||||
|
|
||||||
|
without onProve =? context.onProve:
|
||||||
|
raiseAssert "onProve callback not set"
|
||||||
|
|
||||||
|
without slotIndex =? data.slotIndex:
|
||||||
|
raiseAssert("no slot index assigned")
|
||||||
|
|
||||||
|
debug "Generating initial proof", requestId = $data.requestId
|
||||||
|
let proof = await onProve(Slot(request: request, slotIndex: slotIndex))
|
||||||
|
debug "Finished proof calculation", requestId = $data.requestId
|
||||||
|
|
||||||
|
return some State(SaleFilling(proof: proof))
|
|
@ -0,0 +1,38 @@
|
||||||
|
import pkg/chronicles
|
||||||
|
import ../../market
|
||||||
|
import ../statemachine
|
||||||
|
import ../salesagent
|
||||||
|
import ./errorhandling
|
||||||
|
import ./cancelled
|
||||||
|
import ./failed
|
||||||
|
import ./finished
|
||||||
|
|
||||||
|
logScope:
|
||||||
|
topics = "marketplace sales payout"
|
||||||
|
|
||||||
|
type
|
||||||
|
SalePayout* = ref object of ErrorHandlingState
|
||||||
|
|
||||||
|
method `$`*(state: SalePayout): string = "SalePayout"
|
||||||
|
|
||||||
|
method onCancelled*(state: SalePayout, request: StorageRequest): ?State =
|
||||||
|
return some State(SaleCancelled())
|
||||||
|
|
||||||
|
method onFailed*(state: SalePayout, request: StorageRequest): ?State =
|
||||||
|
return some State(SaleFailed())
|
||||||
|
|
||||||
|
method run(state: SalePayout, machine: Machine): Future[?State] {.async.} =
|
||||||
|
let data = SalesAgent(machine).data
|
||||||
|
let market = SalesAgent(machine).context.market
|
||||||
|
|
||||||
|
without request =? data.request:
|
||||||
|
raiseAssert "no sale request"
|
||||||
|
|
||||||
|
without slotIndex =? data.slotIndex:
|
||||||
|
raiseAssert("no slot index assigned")
|
||||||
|
|
||||||
|
let slot = Slot(request: request, slotIndex: slotIndex)
|
||||||
|
debug "Collecting finished slot's reward", requestId = $data.requestId, slotIndex
|
||||||
|
await market.freeSlot(slot.id)
|
||||||
|
|
||||||
|
return some State(SaleFinished())
|
|
@ -15,7 +15,7 @@ type
|
||||||
SalePreparing* = ref object of ErrorHandlingState
|
SalePreparing* = ref object of ErrorHandlingState
|
||||||
|
|
||||||
logScope:
|
logScope:
|
||||||
topics = "sales preparing"
|
topics = "marketplace sales preparing"
|
||||||
|
|
||||||
method `$`*(state: SalePreparing): string = "SalePreparing"
|
method `$`*(state: SalePreparing): string = "SalePreparing"
|
||||||
|
|
||||||
|
|
|
@ -1,29 +1,106 @@
|
||||||
|
import std/options
|
||||||
import pkg/chronicles
|
import pkg/chronicles
|
||||||
|
import ../../clock
|
||||||
import ../statemachine
|
import ../statemachine
|
||||||
import ../salesagent
|
import ../salesagent
|
||||||
|
import ../salescontext
|
||||||
import ./errorhandling
|
import ./errorhandling
|
||||||
import ./filling
|
|
||||||
import ./cancelled
|
import ./cancelled
|
||||||
import ./failed
|
import ./failed
|
||||||
import ./filled
|
import ./errored
|
||||||
|
import ./payout
|
||||||
|
|
||||||
logScope:
|
logScope:
|
||||||
topics = "marketplace sales proving"
|
topics = "marketplace sales proving"
|
||||||
|
|
||||||
type
|
type
|
||||||
|
SlotNotFilledError* = object of CatchableError
|
||||||
SaleProving* = ref object of ErrorHandlingState
|
SaleProving* = ref object of ErrorHandlingState
|
||||||
|
loop: Future[void]
|
||||||
|
|
||||||
|
method prove*(
|
||||||
|
state: SaleProving,
|
||||||
|
slot: Slot,
|
||||||
|
onProve: OnProve,
|
||||||
|
market: Market,
|
||||||
|
currentPeriod: Period
|
||||||
|
) {.base, async.} =
|
||||||
|
try:
|
||||||
|
let proof = await onProve(slot)
|
||||||
|
debug "Submitting proof", currentPeriod = currentPeriod, slotId = $slot.id
|
||||||
|
await market.submitProof(slot.id, proof)
|
||||||
|
except CatchableError as e:
|
||||||
|
error "Submitting proof failed", msg = e.msg
|
||||||
|
|
||||||
|
proc proveLoop(
|
||||||
|
state: SaleProving,
|
||||||
|
market: Market,
|
||||||
|
clock: Clock,
|
||||||
|
request: StorageRequest,
|
||||||
|
slotIndex: UInt256,
|
||||||
|
onProve: OnProve
|
||||||
|
) {.async.} =
|
||||||
|
|
||||||
|
let slot = Slot(request: request, slotIndex: slotIndex)
|
||||||
|
let slotId = slot.id
|
||||||
|
|
||||||
|
logScope:
|
||||||
|
period = currentPeriod
|
||||||
|
requestId = $request.id
|
||||||
|
slotIndex
|
||||||
|
slotId = $slot.id
|
||||||
|
|
||||||
|
proc getCurrentPeriod(): Future[Period] {.async.} =
|
||||||
|
let periodicity = await market.periodicity()
|
||||||
|
return periodicity.periodOf(clock.now().u256)
|
||||||
|
|
||||||
|
proc waitUntilPeriod(period: Period) {.async.} =
|
||||||
|
let periodicity = await market.periodicity()
|
||||||
|
await clock.waitUntil(periodicity.periodStart(period).truncate(int64))
|
||||||
|
|
||||||
|
while true:
|
||||||
|
let currentPeriod = await getCurrentPeriod()
|
||||||
|
let slotState = await market.slotState(slot.id)
|
||||||
|
if slotState == SlotState.Finished:
|
||||||
|
debug "Slot reached finished state", period = currentPeriod
|
||||||
|
return
|
||||||
|
|
||||||
|
if slotState != SlotState.Filled:
|
||||||
|
raise newException(SlotNotFilledError, "Slot is not in Filled state!")
|
||||||
|
|
||||||
|
debug "Proving for new period", period = currentPeriod
|
||||||
|
|
||||||
|
if (await market.isProofRequired(slotId)) or (await market.willProofBeRequired(slotId)):
|
||||||
|
debug "Proof is required", period = currentPeriod
|
||||||
|
await state.prove(slot, onProve, market, currentPeriod)
|
||||||
|
|
||||||
|
await waitUntilPeriod(currentPeriod + 1)
|
||||||
|
|
||||||
method `$`*(state: SaleProving): string = "SaleProving"
|
method `$`*(state: SaleProving): string = "SaleProving"
|
||||||
|
|
||||||
method onCancelled*(state: SaleProving, request: StorageRequest): ?State =
|
method onCancelled*(state: SaleProving, request: StorageRequest): ?State =
|
||||||
|
if not state.loop.isNil:
|
||||||
|
if not state.loop.finished:
|
||||||
|
try:
|
||||||
|
waitFor state.loop.cancelAndWait()
|
||||||
|
except CatchableError as e:
|
||||||
|
error "Error during cancelation of prooving loop", msg = e.msg
|
||||||
|
|
||||||
|
state.loop = nil
|
||||||
|
|
||||||
return some State(SaleCancelled())
|
return some State(SaleCancelled())
|
||||||
|
|
||||||
method onFailed*(state: SaleProving, request: StorageRequest): ?State =
|
method onFailed*(state: SaleProving, request: StorageRequest): ?State =
|
||||||
return some State(SaleFailed())
|
if not state.loop.isNil:
|
||||||
|
if not state.loop.finished:
|
||||||
|
try:
|
||||||
|
waitFor state.loop.cancelAndWait()
|
||||||
|
except CatchableError as e:
|
||||||
|
error "Error during cancelation of prooving loop", msg = e.msg
|
||||||
|
|
||||||
method onSlotFilled*(state: SaleProving, requestId: RequestId,
|
state.loop = nil
|
||||||
slotIndex: UInt256): ?State =
|
|
||||||
return some State(SaleFilled())
|
return some State(SaleFailed())
|
||||||
|
|
||||||
method run*(state: SaleProving, machine: Machine): Future[?State] {.async.} =
|
method run*(state: SaleProving, machine: Machine): Future[?State] {.async.} =
|
||||||
let data = SalesAgent(machine).data
|
let data = SalesAgent(machine).data
|
||||||
|
@ -32,14 +109,39 @@ method run*(state: SaleProving, machine: Machine): Future[?State] {.async.} =
|
||||||
without request =? data.request:
|
without request =? data.request:
|
||||||
raiseAssert "no sale request"
|
raiseAssert "no sale request"
|
||||||
|
|
||||||
without onProve =? context.proving.onProve:
|
without onProve =? context.onProve:
|
||||||
raiseAssert "onProve callback not set"
|
raiseAssert "onProve callback not set"
|
||||||
|
|
||||||
without slotIndex =? data.slotIndex:
|
without slotIndex =? data.slotIndex:
|
||||||
raiseAssert("no slot index assigned")
|
raiseAssert("no slot index assigned")
|
||||||
|
|
||||||
debug "Start proof generation", requestId = $data.requestId
|
without market =? context.market:
|
||||||
let proof = await onProve(Slot(request: request, slotIndex: slotIndex))
|
raiseAssert("market not set")
|
||||||
debug "Finished proof generation", requestId = $data.requestId
|
|
||||||
|
|
||||||
return some State(SaleFilling(proof: proof))
|
without clock =? context.clock:
|
||||||
|
raiseAssert("clock not set")
|
||||||
|
|
||||||
|
debug "Start proving", requestId = $data.requestId, slotIndex
|
||||||
|
try:
|
||||||
|
let loop = state.proveLoop(market, clock, request, slotIndex, onProve)
|
||||||
|
state.loop = loop
|
||||||
|
await loop
|
||||||
|
except CancelledError:
|
||||||
|
discard
|
||||||
|
except CatchableError as e:
|
||||||
|
error "Proving failed", msg = e.msg
|
||||||
|
return some State(SaleErrored(error: e))
|
||||||
|
finally:
|
||||||
|
# Cleanup of the proving loop
|
||||||
|
debug "Stopping proving.", requestId = $data.requestId, slotIndex
|
||||||
|
|
||||||
|
if not state.loop.isNil:
|
||||||
|
if not state.loop.finished:
|
||||||
|
try:
|
||||||
|
await state.loop.cancelAndWait()
|
||||||
|
except CatchableError as e:
|
||||||
|
error "Error during cancelation of prooving loop", msg = e.msg
|
||||||
|
|
||||||
|
state.loop = nil
|
||||||
|
|
||||||
|
return some State(SalePayout())
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
import ../../conf
|
||||||
|
when codex_enable_proof_failures:
|
||||||
|
import std/strutils
|
||||||
|
import pkg/chronicles
|
||||||
|
import pkg/stint
|
||||||
|
import pkg/ethers
|
||||||
|
import pkg/ethers/testing
|
||||||
|
|
||||||
|
import ../../contracts/requests
|
||||||
|
import ../../market
|
||||||
|
import ../salescontext
|
||||||
|
import ./proving
|
||||||
|
|
||||||
|
logScope:
|
||||||
|
topics = "marketplace sales simulated-proving"
|
||||||
|
|
||||||
|
type
|
||||||
|
SaleProvingSimulated* = ref object of SaleProving
|
||||||
|
failEveryNProofs*: int
|
||||||
|
proofCount: int
|
||||||
|
|
||||||
|
proc onSubmitProofError(error: ref CatchableError, period: UInt256, slotId: SlotId) =
|
||||||
|
error "Submitting invalid proof failed", period = period, slotId = $slotId, msg = error.msg
|
||||||
|
|
||||||
|
method prove*(state: SaleProvingSimulated, slot: Slot, onProve: OnProve, market: Market, currentPeriod: Period) {.async.} =
|
||||||
|
trace "Processing proving in simulated mode"
|
||||||
|
state.proofCount += 1
|
||||||
|
if state.failEveryNProofs > 0 and
|
||||||
|
state.proofCount mod state.failEveryNProofs == 0:
|
||||||
|
state.proofCount = 0
|
||||||
|
|
||||||
|
try:
|
||||||
|
warn "Submitting INVALID proof", period = currentPeriod, slotId = slot.id
|
||||||
|
await market.submitProof(slot.id, newSeq[byte](0))
|
||||||
|
except ProviderError as e:
|
||||||
|
if not e.revertReason.contains("Invalid proof"):
|
||||||
|
onSubmitProofError(e, currentPeriod, slot.id)
|
||||||
|
except CatchableError as e:
|
||||||
|
onSubmitProofError(e, currentPeriod, slot.id)
|
||||||
|
else:
|
||||||
|
await procCall SaleProving(state).prove(slot, onProve, market, currentPeriod)
|
|
@ -1,3 +1,4 @@
|
||||||
|
import pkg/chronicles
|
||||||
import ../statemachine
|
import ../statemachine
|
||||||
import ../salesagent
|
import ../salesagent
|
||||||
import ./filled
|
import ./filled
|
||||||
|
@ -6,6 +7,9 @@ import ./failed
|
||||||
import ./errored
|
import ./errored
|
||||||
import ./cancelled
|
import ./cancelled
|
||||||
|
|
||||||
|
logScope:
|
||||||
|
topics = "marketplace sales unknown"
|
||||||
|
|
||||||
type
|
type
|
||||||
SaleUnknown* = ref object of SaleState
|
SaleUnknown* = ref object of SaleState
|
||||||
SaleUnknownError* = object of CatchableError
|
SaleUnknownError* = object of CatchableError
|
||||||
|
|
|
@ -137,9 +137,9 @@ If your file is downloaded and identical to the file you uploaded, then this man
|
||||||
curl --location 'http://localhost:8081/api/codex/v1/sales/availability' \
|
curl --location 'http://localhost:8081/api/codex/v1/sales/availability' \
|
||||||
--header 'Content-Type: application/json' \
|
--header 'Content-Type: application/json' \
|
||||||
--data '{
|
--data '{
|
||||||
"size": "0xF4240",
|
"size": "1000000",
|
||||||
"duration": "0xE10",
|
"duration": "3600",
|
||||||
"minPrice": "0x3E8"
|
"minPrice": "1000"
|
||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -151,18 +151,27 @@ This informs your node that you are available to store 1MB of data for a duratio
|
||||||
curl --location 'http://localhost:8080/api/codex/v1/storage/request/<CID>' \
|
curl --location 'http://localhost:8080/api/codex/v1/storage/request/<CID>' \
|
||||||
--header 'Content-Type: application/json' \
|
--header 'Content-Type: application/json' \
|
||||||
--data '{
|
--data '{
|
||||||
"reward": "0x400",
|
"reward": "1024",
|
||||||
"duration": "0x78",
|
"duration": "120",
|
||||||
"proofProbability": "0x10"
|
"proofProbability": "8"
|
||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
|
|
||||||
This creates a storage Request for `<CID>` (that you have to fill in) for
|
This creates a storage Request for `<CID>` (that you have to fill in) for
|
||||||
duration of 2 minutes and with reward of 1024 tokens. It expects hosts to
|
duration of 2 minutes and with reward of 1024 tokens. It expects hosts to
|
||||||
provide a storage proof once every 16 periods on average.
|
provide a storage proof once every 8 periods on average.
|
||||||
|
|
||||||
It returns Request ID which you can then use to query for the Request's state as follows:
|
It returns Request ID which you can then use to query for the Request's state as follows:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl --location 'http://localhost:8080/api/codex/v1/storage/purchases/<RequestID>'
|
curl --location 'http://localhost:8080/api/codex/v1/storage/purchases/<RequestID>'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
When using the Ganache blockchain, there are some deviations from the expected behavior, mainly linked to how blocks are mined, which affects certain functionalities in the Sales module.
|
||||||
|
Therefore, if you are manually testing processes such as payout collection after a request is finished or proof submissions, you need to mine some blocks manually for it to work correctly. You can do this by using the following curl command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"evm_mine","params":[],"id":67}' 127.0.0.1:8545
|
||||||
|
```
|
||||||
|
|
|
@ -5,6 +5,7 @@ import pkg/codex/sales/salesagent
|
||||||
import pkg/codex/sales/salescontext
|
import pkg/codex/sales/salescontext
|
||||||
import pkg/codex/sales/states/filled
|
import pkg/codex/sales/states/filled
|
||||||
import pkg/codex/sales/states/errored
|
import pkg/codex/sales/states/errored
|
||||||
|
import pkg/codex/sales/states/proving
|
||||||
import pkg/codex/sales/states/finished
|
import pkg/codex/sales/states/finished
|
||||||
import ../../helpers/mockmarket
|
import ../../helpers/mockmarket
|
||||||
import ../../examples
|
import ../../examples
|
||||||
|
@ -33,11 +34,11 @@ checksuite "sales state 'filled'":
|
||||||
StorageRequest.none)
|
StorageRequest.none)
|
||||||
state = SaleFilled.new()
|
state = SaleFilled.new()
|
||||||
|
|
||||||
test "switches to finished state when slot is filled by me":
|
test "switches to proving state when slot is filled by me":
|
||||||
slot.host = await market.getSigner()
|
slot.host = await market.getSigner()
|
||||||
market.filled = @[slot]
|
market.filled = @[slot]
|
||||||
let next = await state.run(agent)
|
let next = await state.run(agent)
|
||||||
check !next of SaleFinished
|
check !next of SaleProving
|
||||||
|
|
||||||
test "switches to error state when slot is filled by another host":
|
test "switches to error state when slot is filled by another host":
|
||||||
slot.host = Address.example
|
slot.host = Address.example
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
import pkg/asynctest
|
||||||
|
import pkg/questionable
|
||||||
|
import pkg/chronos
|
||||||
|
import pkg/codex/contracts/requests
|
||||||
|
import pkg/codex/sales/states/initialproving
|
||||||
|
import pkg/codex/sales/states/cancelled
|
||||||
|
import pkg/codex/sales/states/failed
|
||||||
|
import pkg/codex/sales/states/filling
|
||||||
|
import pkg/codex/sales/salesagent
|
||||||
|
import pkg/codex/sales/salescontext
|
||||||
|
import ../../examples
|
||||||
|
import ../../helpers
|
||||||
|
|
||||||
|
asyncchecksuite "sales state 'initialproving'":
|
||||||
|
|
||||||
|
let proof = exampleProof()
|
||||||
|
let request = StorageRequest.example
|
||||||
|
let slotIndex = (request.ask.slots div 2).u256
|
||||||
|
|
||||||
|
var state: SaleInitialProving
|
||||||
|
var agent: SalesAgent
|
||||||
|
|
||||||
|
setup:
|
||||||
|
let onProve = proc (slot: Slot): Future[seq[byte]] {.async.} =
|
||||||
|
return proof
|
||||||
|
let context = SalesContext(onProve: onProve.some)
|
||||||
|
agent = newSalesAgent(context,
|
||||||
|
request.id,
|
||||||
|
slotIndex,
|
||||||
|
request.some)
|
||||||
|
state = SaleInitialProving.new()
|
||||||
|
|
||||||
|
test "switches to cancelled state when request expires":
|
||||||
|
let next = state.onCancelled(request)
|
||||||
|
check !next of SaleCancelled
|
||||||
|
|
||||||
|
test "switches to failed state when request fails":
|
||||||
|
let next = state.onFailed(request)
|
||||||
|
check !next of SaleFailed
|
||||||
|
|
||||||
|
test "switches to filling state when initial proving is complete":
|
||||||
|
let next = await state.run(agent)
|
||||||
|
check !next of SaleFilling
|
||||||
|
check SaleFilling(!next).proof == proof
|
|
@ -1,22 +1,45 @@
|
||||||
import std/unittest
|
import pkg/asynctest
|
||||||
|
import pkg/chronos
|
||||||
import pkg/questionable
|
import pkg/questionable
|
||||||
import pkg/codex/contracts/requests
|
import pkg/codex/contracts/requests
|
||||||
import pkg/codex/sales/states/proving
|
import pkg/codex/sales/states/proving
|
||||||
import pkg/codex/sales/states/cancelled
|
import pkg/codex/sales/states/cancelled
|
||||||
import pkg/codex/sales/states/failed
|
import pkg/codex/sales/states/failed
|
||||||
import pkg/codex/sales/states/filled
|
import pkg/codex/sales/states/payout
|
||||||
|
import pkg/codex/sales/salesagent
|
||||||
|
import pkg/codex/sales/salescontext
|
||||||
import ../../examples
|
import ../../examples
|
||||||
import ../../helpers
|
import ../../helpers
|
||||||
|
import ../../helpers/mockmarket
|
||||||
|
import ../../helpers/mockclock
|
||||||
|
|
||||||
checksuite "sales state 'proving'":
|
asyncchecksuite "sales state 'proving'":
|
||||||
|
|
||||||
let request = StorageRequest.example
|
let slot = Slot.example
|
||||||
let slotIndex = (request.ask.slots div 2).u256
|
let request = slot.request
|
||||||
|
let proof = exampleProof()
|
||||||
|
|
||||||
|
var market: MockMarket
|
||||||
|
var clock: MockClock
|
||||||
|
var agent: SalesAgent
|
||||||
var state: SaleProving
|
var state: SaleProving
|
||||||
|
|
||||||
setup:
|
setup:
|
||||||
|
clock = MockClock.new()
|
||||||
|
market = MockMarket.new()
|
||||||
|
let onProve = proc (slot: Slot): Future[seq[byte]] {.async.} =
|
||||||
|
return proof
|
||||||
|
let context = SalesContext(market: market, clock: clock, onProve: onProve.some)
|
||||||
|
agent = newSalesAgent(context,
|
||||||
|
request.id,
|
||||||
|
slot.slotIndex,
|
||||||
|
request.some)
|
||||||
state = SaleProving.new()
|
state = SaleProving.new()
|
||||||
|
|
||||||
|
proc advanceToNextPeriod(market: Market) {.async.} =
|
||||||
|
let periodicity = await market.periodicity()
|
||||||
|
clock.advance(periodicity.seconds.truncate(int64))
|
||||||
|
|
||||||
test "switches to cancelled state when request expires":
|
test "switches to cancelled state when request expires":
|
||||||
let next = state.onCancelled(request)
|
let next = state.onCancelled(request)
|
||||||
check !next of SaleCancelled
|
check !next of SaleCancelled
|
||||||
|
@ -25,7 +48,35 @@ checksuite "sales state 'proving'":
|
||||||
let next = state.onFailed(request)
|
let next = state.onFailed(request)
|
||||||
check !next of SaleFailed
|
check !next of SaleFailed
|
||||||
|
|
||||||
test "switches to filled state when slot is filled":
|
test "submits proofs":
|
||||||
let next = state.onSlotFilled(request.id, slotIndex)
|
var receivedIds: seq[SlotId]
|
||||||
check !next of SaleFilled
|
var receivedProofs: seq[seq[byte]]
|
||||||
|
|
||||||
|
proc onProofSubmission(id: SlotId, proof: seq[byte]) =
|
||||||
|
receivedIds.add(id)
|
||||||
|
receivedProofs.add(proof)
|
||||||
|
|
||||||
|
let subscription = await market.subscribeProofSubmission(onProofSubmission)
|
||||||
|
market.slotState[slot.id] = SlotState.Filled
|
||||||
|
|
||||||
|
let future = state.run(agent)
|
||||||
|
|
||||||
|
market.setProofRequired(slot.id, true)
|
||||||
|
await market.advanceToNextPeriod()
|
||||||
|
|
||||||
|
check eventuallyCheck receivedIds == @[slot.id] and receivedProofs == @[proof]
|
||||||
|
|
||||||
|
await future.cancelAndWait()
|
||||||
|
await subscription.unsubscribe()
|
||||||
|
|
||||||
|
test "switches to payout state when request is finished":
|
||||||
|
market.slotState[slot.id] = SlotState.Filled
|
||||||
|
|
||||||
|
let future = state.run(agent)
|
||||||
|
|
||||||
|
market.slotState[slot.id] = SlotState.Finished
|
||||||
|
await market.advanceToNextPeriod()
|
||||||
|
|
||||||
|
check eventuallyCheck future.finished
|
||||||
|
check !(future.read()) of SalePayout
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
import pkg/asynctest
|
||||||
|
import pkg/chronos
|
||||||
|
import pkg/questionable
|
||||||
|
import pkg/codex/contracts/requests
|
||||||
|
import pkg/codex/sales/states/provingsimulated
|
||||||
|
import pkg/codex/sales/states/proving
|
||||||
|
import pkg/codex/sales/states/cancelled
|
||||||
|
import pkg/codex/sales/states/failed
|
||||||
|
import pkg/codex/sales/states/payout
|
||||||
|
import pkg/codex/sales/salesagent
|
||||||
|
import pkg/codex/sales/salescontext
|
||||||
|
import ../../examples
|
||||||
|
import ../../helpers
|
||||||
|
import ../../helpers/mockmarket
|
||||||
|
import ../../helpers/mockclock
|
||||||
|
|
||||||
|
asyncchecksuite "sales state 'simulated-proving'":
|
||||||
|
|
||||||
|
let slot = Slot.example
|
||||||
|
let request = slot.request
|
||||||
|
let proof = exampleProof()
|
||||||
|
let failEveryNProofs = 3
|
||||||
|
let totalProofs = 6
|
||||||
|
|
||||||
|
var market: MockMarket
|
||||||
|
var clock: MockClock
|
||||||
|
var agent: SalesAgent
|
||||||
|
var state: SaleProvingSimulated
|
||||||
|
|
||||||
|
var proofSubmitted: Future[void] = newFuture[void]("proofSubmitted")
|
||||||
|
var submitted: seq[seq[byte]]
|
||||||
|
var subscription: Subscription
|
||||||
|
|
||||||
|
setup:
|
||||||
|
clock = MockClock.new()
|
||||||
|
|
||||||
|
proc onProofSubmission(id: SlotId, proof: seq[byte]) =
|
||||||
|
submitted.add(proof)
|
||||||
|
proofSubmitted.complete()
|
||||||
|
proofSubmitted = newFuture[void]("proofSubmitted")
|
||||||
|
|
||||||
|
market = MockMarket.new()
|
||||||
|
market.slotState[slot.id] = SlotState.Filled
|
||||||
|
market.setProofRequired(slot.id, true)
|
||||||
|
subscription = await market.subscribeProofSubmission(onProofSubmission)
|
||||||
|
|
||||||
|
let onProve = proc (slot: Slot): Future[seq[byte]] {.async.} =
|
||||||
|
return proof
|
||||||
|
let context = SalesContext(market: market, clock: clock, onProve: onProve.some)
|
||||||
|
agent = newSalesAgent(context,
|
||||||
|
request.id,
|
||||||
|
slot.slotIndex,
|
||||||
|
request.some)
|
||||||
|
state = SaleProvingSimulated.new()
|
||||||
|
state.failEveryNProofs = failEveryNProofs
|
||||||
|
|
||||||
|
teardown:
|
||||||
|
await subscription.unsubscribe()
|
||||||
|
|
||||||
|
proc advanceToNextPeriod(market: Market) {.async.} =
|
||||||
|
let periodicity = await market.periodicity()
|
||||||
|
clock.advance(periodicity.seconds.truncate(int64))
|
||||||
|
|
||||||
|
proc waitForProvingRounds(market: Market, rounds: int) {.async.} =
|
||||||
|
var rnds = rounds - 1 # proof round runs prior to advancing
|
||||||
|
while rnds > 0:
|
||||||
|
await market.advanceToNextPeriod()
|
||||||
|
await proofSubmitted
|
||||||
|
rnds -= 1
|
||||||
|
|
||||||
|
test "switches to cancelled state when request expires":
|
||||||
|
let next = state.onCancelled(request)
|
||||||
|
check !next of SaleCancelled
|
||||||
|
|
||||||
|
test "switches to failed state when request fails":
|
||||||
|
let next = state.onFailed(request)
|
||||||
|
check !next of SaleFailed
|
||||||
|
|
||||||
|
test "submits invalid proof every 3 proofs":
|
||||||
|
let future = state.run(agent)
|
||||||
|
|
||||||
|
await market.waitForProvingRounds(totalProofs)
|
||||||
|
check submitted == @[proof, proof, @[], proof, proof, @[]]
|
||||||
|
|
||||||
|
await future.cancelAndWait()
|
||||||
|
|
||||||
|
test "switches to payout state when request is finished":
|
||||||
|
market.slotState[slot.id] = SlotState.Filled
|
||||||
|
|
||||||
|
let future = state.run(agent)
|
||||||
|
|
||||||
|
market.slotState[slot.id] = SlotState.Finished
|
||||||
|
await market.advanceToNextPeriod()
|
||||||
|
|
||||||
|
check eventuallyCheck future.finished
|
||||||
|
check !(future.read()) of SalePayout
|
|
@ -13,7 +13,6 @@ import pkg/codex/sales/salescontext
|
||||||
import pkg/codex/sales/reservations
|
import pkg/codex/sales/reservations
|
||||||
import pkg/codex/sales/slotqueue
|
import pkg/codex/sales/slotqueue
|
||||||
import pkg/codex/stores/repostore
|
import pkg/codex/stores/repostore
|
||||||
import pkg/codex/proving
|
|
||||||
import pkg/codex/blocktype as bt
|
import pkg/codex/blocktype as bt
|
||||||
import pkg/codex/node
|
import pkg/codex/node
|
||||||
import ../helpers/mockmarket
|
import ../helpers/mockmarket
|
||||||
|
@ -29,7 +28,6 @@ asyncchecksuite "Sales - start":
|
||||||
var sales: Sales
|
var sales: Sales
|
||||||
var market: MockMarket
|
var market: MockMarket
|
||||||
var clock: MockClock
|
var clock: MockClock
|
||||||
var proving: Proving
|
|
||||||
var reservations: Reservations
|
var reservations: Reservations
|
||||||
var repo: RepoStore
|
var repo: RepoStore
|
||||||
var queue: SlotQueue
|
var queue: SlotQueue
|
||||||
|
@ -52,19 +50,18 @@ asyncchecksuite "Sales - start":
|
||||||
|
|
||||||
market = MockMarket.new()
|
market = MockMarket.new()
|
||||||
clock = MockClock.new()
|
clock = MockClock.new()
|
||||||
proving = Proving.new()
|
|
||||||
let repoDs = SQLiteDatastore.new(Memory).tryGet()
|
let repoDs = SQLiteDatastore.new(Memory).tryGet()
|
||||||
let metaDs = SQLiteDatastore.new(Memory).tryGet()
|
let metaDs = SQLiteDatastore.new(Memory).tryGet()
|
||||||
repo = RepoStore.new(repoDs, metaDs)
|
repo = RepoStore.new(repoDs, metaDs)
|
||||||
await repo.start()
|
await repo.start()
|
||||||
sales = Sales.new(market, clock, proving, repo)
|
sales = Sales.new(market, clock, repo)
|
||||||
reservations = sales.context.reservations
|
reservations = sales.context.reservations
|
||||||
sales.onStore = proc(request: StorageRequest,
|
sales.onStore = proc(request: StorageRequest,
|
||||||
slot: UInt256,
|
slot: UInt256,
|
||||||
onBatch: BatchProc): Future[?!void] {.async.} =
|
onBatch: BatchProc): Future[?!void] {.async.} =
|
||||||
return success()
|
return success()
|
||||||
queue = sales.context.slotQueue
|
queue = sales.context.slotQueue
|
||||||
proving.onProve = proc(slot: Slot): Future[seq[byte]] {.async.} =
|
sales.onProve = proc(slot: Slot): Future[seq[byte]] {.async.} =
|
||||||
return proof
|
return proof
|
||||||
itemsProcessed = @[]
|
itemsProcessed = @[]
|
||||||
request.expiry = (clock.now() + 42).u256
|
request.expiry = (clock.now() + 42).u256
|
||||||
|
@ -119,7 +116,6 @@ asyncchecksuite "Sales":
|
||||||
var sales: Sales
|
var sales: Sales
|
||||||
var market: MockMarket
|
var market: MockMarket
|
||||||
var clock: MockClock
|
var clock: MockClock
|
||||||
var proving: Proving
|
|
||||||
var reservations: Reservations
|
var reservations: Reservations
|
||||||
var repo: RepoStore
|
var repo: RepoStore
|
||||||
var queue: SlotQueue
|
var queue: SlotQueue
|
||||||
|
@ -152,19 +148,18 @@ asyncchecksuite "Sales":
|
||||||
market.activeSlots[me] = @[]
|
market.activeSlots[me] = @[]
|
||||||
|
|
||||||
clock = MockClock.new()
|
clock = MockClock.new()
|
||||||
proving = Proving.new()
|
|
||||||
let repoDs = SQLiteDatastore.new(Memory).tryGet()
|
let repoDs = SQLiteDatastore.new(Memory).tryGet()
|
||||||
let metaDs = SQLiteDatastore.new(Memory).tryGet()
|
let metaDs = SQLiteDatastore.new(Memory).tryGet()
|
||||||
repo = RepoStore.new(repoDs, metaDs)
|
repo = RepoStore.new(repoDs, metaDs)
|
||||||
await repo.start()
|
await repo.start()
|
||||||
sales = Sales.new(market, clock, proving, repo)
|
sales = Sales.new(market, clock, repo)
|
||||||
reservations = sales.context.reservations
|
reservations = sales.context.reservations
|
||||||
sales.onStore = proc(request: StorageRequest,
|
sales.onStore = proc(request: StorageRequest,
|
||||||
slot: UInt256,
|
slot: UInt256,
|
||||||
onBatch: BatchProc): Future[?!void] {.async.} =
|
onBatch: BatchProc): Future[?!void] {.async.} =
|
||||||
return success()
|
return success()
|
||||||
queue = sales.context.slotQueue
|
queue = sales.context.slotQueue
|
||||||
proving.onProve = proc(slot: Slot): Future[seq[byte]] {.async.} =
|
sales.onProve = proc(slot: Slot): Future[seq[byte]] {.async.} =
|
||||||
return proof
|
return proof
|
||||||
await sales.start()
|
await sales.start()
|
||||||
request.expiry = (clock.now() + 42).u256
|
request.expiry = (clock.now() + 42).u256
|
||||||
|
@ -363,7 +358,7 @@ asyncchecksuite "Sales":
|
||||||
|
|
||||||
test "handles errors during state run":
|
test "handles errors during state run":
|
||||||
var saleFailed = false
|
var saleFailed = false
|
||||||
proving.onProve = proc(slot: Slot): Future[seq[byte]] {.async.} =
|
sales.onProve = proc(slot: Slot): Future[seq[byte]] {.async.} =
|
||||||
# raise exception so machine.onError is called
|
# raise exception so machine.onError is called
|
||||||
raise newException(ValueError, "some error")
|
raise newException(ValueError, "some error")
|
||||||
|
|
||||||
|
@ -389,7 +384,7 @@ asyncchecksuite "Sales":
|
||||||
test "generates proof of storage":
|
test "generates proof of storage":
|
||||||
var provingRequest: StorageRequest
|
var provingRequest: StorageRequest
|
||||||
var provingSlot: UInt256
|
var provingSlot: UInt256
|
||||||
proving.onProve = proc(slot: Slot): Future[seq[byte]] {.async.} =
|
sales.onProve = proc(slot: Slot): Future[seq[byte]] {.async.} =
|
||||||
provingRequest = slot.request
|
provingRequest = slot.request
|
||||||
provingSlot = slot.slotIndex
|
provingSlot = slot.slotIndex
|
||||||
check isOk await reservations.reserve(availability)
|
check isOk await reservations.reserve(availability)
|
||||||
|
@ -425,7 +420,7 @@ asyncchecksuite "Sales":
|
||||||
test "calls onClear when storage becomes available again":
|
test "calls onClear when storage becomes available again":
|
||||||
# fail the proof intentionally to trigger `agent.finish(success=false)`,
|
# fail the proof intentionally to trigger `agent.finish(success=false)`,
|
||||||
# which then calls the onClear callback
|
# which then calls the onClear callback
|
||||||
proving.onProve = proc(slot: Slot): Future[seq[byte]] {.async.} =
|
sales.onProve = proc(slot: Slot): Future[seq[byte]] {.async.} =
|
||||||
raise newException(IOError, "proof failed")
|
raise newException(IOError, "proof failed")
|
||||||
var clearedRequest: StorageRequest
|
var clearedRequest: StorageRequest
|
||||||
var clearedSlotIndex: UInt256
|
var clearedSlotIndex: UInt256
|
||||||
|
@ -462,17 +457,6 @@ asyncchecksuite "Sales":
|
||||||
clock.set(request.expiry.truncate(int64))
|
clock.set(request.expiry.truncate(int64))
|
||||||
check eventually (await reservations.allAvailabilities) == @[availability]
|
check eventually (await reservations.allAvailabilities) == @[availability]
|
||||||
|
|
||||||
test "adds proving for slot when slot is filled":
|
|
||||||
var soldSlotIndex: UInt256
|
|
||||||
sales.onSale = proc(request: StorageRequest,
|
|
||||||
slotIndex: UInt256) =
|
|
||||||
soldSlotIndex = slotIndex
|
|
||||||
check proving.slots.len == 0
|
|
||||||
check isOk await reservations.reserve(availability)
|
|
||||||
await market.requestStorage(request)
|
|
||||||
check eventuallyCheck proving.slots.len == 1
|
|
||||||
check proving.slots.contains(Slot(request: request, slotIndex: soldSlotIndex))
|
|
||||||
|
|
||||||
test "loads active slots from market":
|
test "loads active slots from market":
|
||||||
let me = await market.getSigner()
|
let me = await market.getSigner()
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,9 @@ import ./states/testunknown
|
||||||
import ./states/testdownloading
|
import ./states/testdownloading
|
||||||
import ./states/testfilling
|
import ./states/testfilling
|
||||||
import ./states/testfinished
|
import ./states/testfinished
|
||||||
import ./states/testproving
|
import ./states/testinitialproving
|
||||||
import ./states/testfilled
|
import ./states/testfilled
|
||||||
|
import ./states/testproving
|
||||||
|
import ./states/testsimulatedproving
|
||||||
|
|
||||||
{.warning[UnusedImport]: off.}
|
{.warning[UnusedImport]: off.}
|
||||||
|
|
|
@ -1,188 +0,0 @@
|
||||||
import std/sequtils
|
|
||||||
import pkg/asynctest
|
|
||||||
import pkg/chronos
|
|
||||||
import pkg/codex/proving
|
|
||||||
import ./helpers/mockmarket
|
|
||||||
import ./helpers/mockclock
|
|
||||||
import ./helpers/eventually
|
|
||||||
import ./examples
|
|
||||||
import ./helpers
|
|
||||||
|
|
||||||
asyncchecksuite "Proving":
|
|
||||||
|
|
||||||
var proving: Proving
|
|
||||||
var market: MockMarket
|
|
||||||
var clock: MockClock
|
|
||||||
|
|
||||||
setup:
|
|
||||||
market = MockMarket.new()
|
|
||||||
clock = MockClock.new()
|
|
||||||
proving = Proving.new(market, clock)
|
|
||||||
await proving.start()
|
|
||||||
|
|
||||||
teardown:
|
|
||||||
await proving.stop()
|
|
||||||
|
|
||||||
proc advanceToNextPeriod(market: MockMarket) {.async.} =
|
|
||||||
let periodicity = await market.periodicity()
|
|
||||||
clock.advance(periodicity.seconds.truncate(int64))
|
|
||||||
|
|
||||||
test "maintains a list of slots to watch":
|
|
||||||
let slot1, slot2 = Slot.example
|
|
||||||
check proving.slots.len == 0
|
|
||||||
proving.add(slot1)
|
|
||||||
check proving.slots.contains(slot1)
|
|
||||||
proving.add(slot2)
|
|
||||||
check proving.slots.contains(slot1)
|
|
||||||
check proving.slots.contains(slot2)
|
|
||||||
|
|
||||||
test "removes duplicate slots":
|
|
||||||
let slot = Slot.example
|
|
||||||
proving.add(slot)
|
|
||||||
proving.add(slot)
|
|
||||||
check proving.slots.len == 1
|
|
||||||
|
|
||||||
test "invokes callback when proof is required":
|
|
||||||
let slot = Slot.example
|
|
||||||
proving.add(slot)
|
|
||||||
var called: bool
|
|
||||||
proc onProve(slot: Slot): Future[seq[byte]] {.async.} =
|
|
||||||
called = true
|
|
||||||
proving.onProve = onProve
|
|
||||||
market.slotState[slot.id] = SlotState.Filled
|
|
||||||
market.setProofRequired(slot.id, true)
|
|
||||||
await market.advanceToNextPeriod()
|
|
||||||
check eventually called
|
|
||||||
|
|
||||||
test "callback receives slot for which proof is required":
|
|
||||||
let slot1, slot2 = Slot.example
|
|
||||||
proving.add(slot1)
|
|
||||||
proving.add(slot2)
|
|
||||||
var callbackSlots: seq[Slot]
|
|
||||||
proc onProve(slot: Slot): Future[seq[byte]] {.async.} =
|
|
||||||
callbackSlots.add(slot)
|
|
||||||
proving.onProve = onProve
|
|
||||||
market.slotState[slot1.id] = SlotState.Filled
|
|
||||||
market.slotState[slot2.id] = SlotState.Filled
|
|
||||||
market.setProofRequired(slot1.id, true)
|
|
||||||
await market.advanceToNextPeriod()
|
|
||||||
check eventually callbackSlots == @[slot1]
|
|
||||||
market.setProofRequired(slot1.id, false)
|
|
||||||
market.setProofRequired(slot2.id, true)
|
|
||||||
await market.advanceToNextPeriod()
|
|
||||||
check eventually callbackSlots == @[slot1, slot2]
|
|
||||||
|
|
||||||
test "invokes callback when proof is about to be required":
|
|
||||||
let slot = Slot.example
|
|
||||||
proving.add(slot)
|
|
||||||
var called: bool
|
|
||||||
proc onProve(slot: Slot): Future[seq[byte]] {.async.} =
|
|
||||||
called = true
|
|
||||||
proving.onProve = onProve
|
|
||||||
market.setProofRequired(slot.id, false)
|
|
||||||
market.setProofToBeRequired(slot.id, true)
|
|
||||||
market.slotState[slot.id] = SlotState.Filled
|
|
||||||
await market.advanceToNextPeriod()
|
|
||||||
check eventually called
|
|
||||||
|
|
||||||
test "stops watching when slot is finished":
|
|
||||||
let slot = Slot.example
|
|
||||||
proving.add(slot)
|
|
||||||
market.setProofEnd(slot.id, clock.now().u256)
|
|
||||||
await market.advanceToNextPeriod()
|
|
||||||
var called: bool
|
|
||||||
proc onProve(slot: Slot): Future[seq[byte]] {.async.} =
|
|
||||||
called = true
|
|
||||||
proving.onProve = onProve
|
|
||||||
market.setProofRequired(slot.id, true)
|
|
||||||
await market.advanceToNextPeriod()
|
|
||||||
market.slotState[slot.id] = SlotState.Finished
|
|
||||||
check eventually (not proving.slots.contains(slot))
|
|
||||||
check not called
|
|
||||||
|
|
||||||
test "submits proofs":
|
|
||||||
let slot = Slot.example
|
|
||||||
let proof = exampleProof()
|
|
||||||
|
|
||||||
proving.onProve = proc (slot: Slot): Future[seq[byte]] {.async.} =
|
|
||||||
return proof
|
|
||||||
|
|
||||||
var receivedIds: seq[SlotId]
|
|
||||||
var receivedProofs: seq[seq[byte]]
|
|
||||||
|
|
||||||
proc onProofSubmission(id: SlotId, proof: seq[byte]) =
|
|
||||||
receivedIds.add(id)
|
|
||||||
receivedProofs.add(proof)
|
|
||||||
|
|
||||||
let subscription = await proving.subscribeProofSubmission(onProofSubmission)
|
|
||||||
|
|
||||||
proving.add(slot)
|
|
||||||
market.slotState[slot.id] = SlotState.Filled
|
|
||||||
market.setProofRequired(slot.id, true)
|
|
||||||
await market.advanceToNextPeriod()
|
|
||||||
|
|
||||||
check eventually receivedIds == @[slot.id] and receivedProofs == @[proof]
|
|
||||||
|
|
||||||
await subscription.unsubscribe()
|
|
||||||
|
|
||||||
suite "Simulated proving":
|
|
||||||
|
|
||||||
var proving: SimulatedProving
|
|
||||||
var subscription: Subscription
|
|
||||||
var market: MockMarket
|
|
||||||
var clock: MockClock
|
|
||||||
var submitted: seq[seq[byte]]
|
|
||||||
var proof: seq[byte]
|
|
||||||
let slot = Slot.example
|
|
||||||
var proofSubmitted: Future[void]
|
|
||||||
|
|
||||||
setup:
|
|
||||||
proof = exampleProof()
|
|
||||||
submitted = @[]
|
|
||||||
market = MockMarket.new()
|
|
||||||
clock = MockClock.new()
|
|
||||||
proofSubmitted = newFuture[void]("proofSubmitted")
|
|
||||||
|
|
||||||
teardown:
|
|
||||||
await subscription.unsubscribe()
|
|
||||||
await proving.stop()
|
|
||||||
|
|
||||||
proc newSimulatedProving(failEveryNProofs: uint) {.async.} =
|
|
||||||
proc onProofSubmission(id: SlotId, proof: seq[byte]) =
|
|
||||||
submitted.add(proof)
|
|
||||||
proofSubmitted.complete()
|
|
||||||
proofSubmitted = newFuture[void]("proofSubmitted")
|
|
||||||
|
|
||||||
proving = SimulatedProving.new(market, clock, failEveryNProofs)
|
|
||||||
proving.onProve = proc (slot: Slot): Future[seq[byte]] {.async.} =
|
|
||||||
return proof
|
|
||||||
subscription = await proving.subscribeProofSubmission(onProofSubmission)
|
|
||||||
proving.add(slot)
|
|
||||||
market.slotState[slot.id] = SlotState.Filled
|
|
||||||
market.setProofRequired(slot.id, true)
|
|
||||||
await proving.start()
|
|
||||||
|
|
||||||
proc advanceToNextPeriod(market: Market) {.async.} =
|
|
||||||
let periodicity = await market.periodicity()
|
|
||||||
clock.advance(periodicity.seconds.truncate(int64))
|
|
||||||
|
|
||||||
proc waitForProvingRounds(market: Market, rounds: uint) {.async.} =
|
|
||||||
var rnds = rounds - 1 # proof round runs prior to advancing
|
|
||||||
while rnds > 0:
|
|
||||||
await market.advanceToNextPeriod()
|
|
||||||
await proofSubmitted
|
|
||||||
rnds -= 1
|
|
||||||
|
|
||||||
test "submits invalid proof every 3 proofs":
|
|
||||||
let failEveryNProofs = 3'u
|
|
||||||
let totalProofs = 6'u
|
|
||||||
await newSimulatedProving(failEveryNProofs)
|
|
||||||
await market.waitForProvingRounds(totalProofs)
|
|
||||||
check submitted == @[proof, proof, @[], proof, proof, @[]]
|
|
||||||
|
|
||||||
test "does not submit invalid proofs when failEveryNProofs is 0":
|
|
||||||
let failEveryNProofs = 0'u
|
|
||||||
let totalProofs = 6'u
|
|
||||||
await newSimulatedProving(failEveryNProofs)
|
|
||||||
await market.waitForProvingRounds(totalProofs)
|
|
||||||
check submitted == proof.repeat(totalProofs)
|
|
|
@ -9,7 +9,6 @@ import ./codex/teststorestream
|
||||||
import ./codex/testpurchasing
|
import ./codex/testpurchasing
|
||||||
import ./codex/testsales
|
import ./codex/testsales
|
||||||
import ./codex/testerasure
|
import ./codex/testerasure
|
||||||
import ./codex/testproving
|
|
||||||
import ./codex/testutils
|
import ./codex/testutils
|
||||||
import ./codex/testclock
|
import ./codex/testclock
|
||||||
import ./codex/testsystemclock
|
import ./codex/testsystemclock
|
||||||
|
|
Loading…
Reference in New Issue