sales: calculate initial proof at start of period

reason: this ensures that the period (and therefore
the challenge) doesn't change while we're calculating
the proof
This commit is contained in:
Mark Spanbroek 2024-02-22 07:20:40 +01:00
parent 2a1ef5d9e6
commit 0aa7590c96
No known key found for this signature in database
GPG Key ID: FBE3E9548D427C00
4 changed files with 59 additions and 5 deletions

View File

@ -1,4 +1,5 @@
import pkg/questionable/results
import ../../clock
import ../../logutils
import ../statemachine
import ../salesagent
@ -25,6 +26,8 @@ method onFailed*(state: SaleInitialProving, request: StorageRequest): ?State =
method run*(state: SaleInitialProving, machine: Machine): Future[?State] {.async.} =
let data = SalesAgent(machine).data
let context = SalesAgent(machine).context
let market = context.market
let clock = context.clock
without request =? data.request:
raiseAssert "no sale request"
@ -32,6 +35,11 @@ method run*(state: SaleInitialProving, machine: Machine): Future[?State] {.async
without onProve =? context.onProve:
raiseAssert "onProve callback not set"
debug "Waiting until next period"
let periodicity = await market.periodicity()
let period = periodicity.periodOf(clock.now().u256)
await clock.waitUntil(periodicity.periodEnd(period).truncate(int64) + 1)
debug "Generating initial proof", requestId = data.requestId
let
slot = Slot(request: request, slotIndex: data.slotIndex)

View File

@ -0,0 +1,8 @@
import pkg/codex/market
import ../../helpers/mockclock
proc advanceToNextPeriod*(clock: MockClock, market: Market) {.async.} =
let periodicity = await market.periodicity()
let period = periodicity.periodOf(clock.now().u256)
let periodEnd = periodicity.periodEnd(period)
clock.set((periodEnd + 1).truncate(int))

View File

@ -14,12 +14,15 @@ import ../../../asynctest
import ../../examples
import ../../helpers
import ../../helpers/mockmarket
import ../../helpers/mockclock
import ../helpers/periods
asyncchecksuite "sales state 'initialproving'":
let proof = Groth16Proof.example
let request = StorageRequest.example
let slotIndex = (request.ask.slots div 2).u256
let market = MockMarket.new()
let clock = MockClock.new()
var state: SaleInitialProving
var agent: SalesAgent
@ -31,7 +34,8 @@ asyncchecksuite "sales state 'initialproving'":
return success(proof)
let context = SalesContext(
onProve: onProve.some,
market: market
market: market,
clock: clock
)
agent = newSalesAgent(context,
request.id,
@ -39,6 +43,12 @@ asyncchecksuite "sales state 'initialproving'":
request.some)
state = SaleInitialProving.new()
proc allowProofToStart {.async.} =
# wait until we're in initialproving state
await sleepAsync(10.millis)
# it won't start proving until the next period
await clock.advanceToNextPeriod(market)
test "switches to cancelled state when request expires":
let next = state.onCancelled(request)
check !next of SaleCancelled
@ -48,7 +58,10 @@ asyncchecksuite "sales state 'initialproving'":
check !next of SaleFailed
test "switches to filling state when initial proving is complete":
let next = await state.run(agent)
let future = state.run(agent)
await allowProofToStart()
let next = await future
check !next of SaleFilling
check SaleFilling(!next).proof == proof
@ -56,6 +69,9 @@ asyncchecksuite "sales state 'initialproving'":
market.proofChallenge = ProofChallenge.example
let future = state.run(agent)
await allowProofToStart()
discard await future
check receivedChallenge == market.proofChallenge
@ -65,12 +81,16 @@ asyncchecksuite "sales state 'initialproving'":
let proofFailedContext = SalesContext(
onProve: onProveFailed.some,
market: market
market: market,
clock: clock
)
agent = newSalesAgent(proofFailedContext,
request.id,
slotIndex,
request.some)
let next = await state.run(agent)
let future = state.run(agent)
await allowProofToStart()
let next = await future
check !next of SaleErrored

View File

@ -19,6 +19,7 @@ import ../helpers/mockmarket
import ../helpers/mockclock
import ../helpers/always
import ../examples
import ./helpers/periods
asyncchecksuite "Sales - start":
let proof = Groth16Proof.example
@ -176,6 +177,12 @@ asyncchecksuite "Sales":
await sales.stop()
await repo.stop()
proc allowRequestToStart {.async.} =
# wait until we're in initialproving state
await sleepAsync(10.millis)
# it won't start proving until the next period
await clock.advanceToNextPeriod(market)
proc getAvailability: Availability =
let key = availability.id.key.get
(waitFor reservations.get(key, Availability)).get
@ -311,7 +318,8 @@ asyncchecksuite "Sales":
createAvailability()
let origSize = availability.size
await market.requestStorage(request)
await sold # allow proving to start
await allowRequestToStart()
await sold
# complete request
market.slotState[request.slotId(slotIndex)] = SlotState.Finished
@ -379,6 +387,8 @@ asyncchecksuite "Sales":
saleFailed = true
createAvailability()
await market.requestStorage(request)
await allowRequestToStart()
check eventually saleFailed
test "makes storage available again when data retrieval fails":
@ -400,12 +410,16 @@ asyncchecksuite "Sales":
return success(Groth16Proof.example)
createAvailability()
await market.requestStorage(request)
await allowRequestToStart()
check eventually provingRequest == request
check provingSlot < request.ask.slots.u256
test "fills a slot":
createAvailability()
await market.requestStorage(request)
await allowRequestToStart()
check eventually market.filled.len > 0
check market.filled[0].requestId == request.id
check market.filled[0].slotIndex < request.ask.slots.u256
@ -421,6 +435,8 @@ asyncchecksuite "Sales":
soldSlotIndex = slotIndex
createAvailability()
await market.requestStorage(request)
await allowRequestToStart()
check eventually soldRequest == request
check soldSlotIndex < request.ask.slots.u256
@ -437,6 +453,8 @@ asyncchecksuite "Sales":
clearedSlotIndex = slotIndex
createAvailability()
await market.requestStorage(request)
await allowRequestToStart()
check eventually clearedRequest == request
check clearedSlotIndex < request.ask.slots.u256