fix(slot-reservations): Avoid slot filled cancellations (#963)
* Avoid cancelling states when slot is filled * improve logging Improves logging for situations where a Sale should be ignored instead of being considered an error, including when reservation is not allowed and when a slot was filled by another host. * remove onSlotFilled unit tests from states
This commit is contained in:
parent
3a2d0926f1
commit
0157ca4c57
|
@ -165,8 +165,14 @@ method fillSlot(market: OnChainMarket,
|
||||||
proof: Groth16Proof,
|
proof: Groth16Proof,
|
||||||
collateral: UInt256) {.async.} =
|
collateral: UInt256) {.async.} =
|
||||||
convertEthersError:
|
convertEthersError:
|
||||||
|
logScope:
|
||||||
|
requestId
|
||||||
|
slotIndex
|
||||||
|
|
||||||
await market.approveFunds(collateral)
|
await market.approveFunds(collateral)
|
||||||
|
trace "calling fillSlot on contract"
|
||||||
discard await market.contract.fillSlot(requestId, slotIndex, proof).confirm(0)
|
discard await market.contract.fillSlot(requestId, slotIndex, proof).confirm(0)
|
||||||
|
trace "fillSlot transaction completed"
|
||||||
|
|
||||||
method freeSlot*(market: OnChainMarket, slotId: SlotId) {.async.} =
|
method freeSlot*(market: OnChainMarket, slotId: SlotId) {.async.} =
|
||||||
convertEthersError:
|
convertEthersError:
|
||||||
|
@ -253,7 +259,12 @@ method reserveSlot*(
|
||||||
slotIndex: UInt256) {.async.} =
|
slotIndex: UInt256) {.async.} =
|
||||||
|
|
||||||
convertEthersError:
|
convertEthersError:
|
||||||
discard await market.contract.reserveSlot(requestId, slotIndex).confirm(0)
|
discard await market.contract.reserveSlot(
|
||||||
|
requestId,
|
||||||
|
slotIndex,
|
||||||
|
# reserveSlot runs out of gas for unknown reason, but 100k gas covers it
|
||||||
|
TransactionOverrides(gasLimit: some 100000.u256)
|
||||||
|
).confirm(0)
|
||||||
|
|
||||||
method canReserveSlot*(
|
method canReserveSlot*(
|
||||||
market: OnChainMarket,
|
market: OnChainMarket,
|
||||||
|
|
|
@ -6,6 +6,8 @@ import ./errorhandling
|
||||||
import ./filled
|
import ./filled
|
||||||
import ./cancelled
|
import ./cancelled
|
||||||
import ./failed
|
import ./failed
|
||||||
|
import ./ignored
|
||||||
|
import ./errored
|
||||||
|
|
||||||
logScope:
|
logScope:
|
||||||
topics = "marketplace sales filling"
|
topics = "marketplace sales filling"
|
||||||
|
@ -22,16 +24,25 @@ method onCancelled*(state: SaleFilling, request: StorageRequest): ?State =
|
||||||
method onFailed*(state: SaleFilling, request: StorageRequest): ?State =
|
method onFailed*(state: SaleFilling, request: StorageRequest): ?State =
|
||||||
return some State(SaleFailed())
|
return some State(SaleFailed())
|
||||||
|
|
||||||
method onSlotFilled*(state: SaleFilling, requestId: RequestId,
|
|
||||||
slotIndex: UInt256): ?State =
|
|
||||||
return some State(SaleFilled())
|
|
||||||
|
|
||||||
method run(state: SaleFilling, machine: Machine): Future[?State] {.async.} =
|
method run(state: SaleFilling, machine: Machine): Future[?State] {.async.} =
|
||||||
let data = SalesAgent(machine).data
|
let data = SalesAgent(machine).data
|
||||||
let market = SalesAgent(machine).context.market
|
let market = SalesAgent(machine).context.market
|
||||||
without (collateral =? data.request.?ask.?collateral):
|
without (collateral =? data.request.?ask.?collateral):
|
||||||
raiseAssert "Request not set"
|
raiseAssert "Request not set"
|
||||||
|
|
||||||
debug "Filling slot", requestId = data.requestId, slotIndex = data.slotIndex
|
logScope:
|
||||||
await market.fillSlot(data.requestId, data.slotIndex, state.proof, collateral)
|
requestId = data.requestId
|
||||||
debug "Waiting for slot filled event...", requestId = $data.requestId, slotIndex = $data.slotIndex
|
slotIndex = data.slotIndex
|
||||||
|
|
||||||
|
debug "Filling slot"
|
||||||
|
try:
|
||||||
|
await market.fillSlot(data.requestId, data.slotIndex, state.proof, collateral)
|
||||||
|
except MarketError as e:
|
||||||
|
if e.msg.contains "Slot is not free":
|
||||||
|
debug "Slot is already filled, ignoring slot"
|
||||||
|
return some State( SaleIgnored(reprocessSlot: false, returnBytes: true) )
|
||||||
|
else:
|
||||||
|
return some State( SaleErrored(error: e) )
|
||||||
|
# other CatchableErrors are handled "automatically" by the ErrorHandlingState
|
||||||
|
|
||||||
|
return some State(SaleFilled())
|
||||||
|
|
|
@ -28,10 +28,6 @@ method onCancelled*(state: SaleSlotReserving, request: StorageRequest): ?State =
|
||||||
method onFailed*(state: SaleSlotReserving, request: StorageRequest): ?State =
|
method onFailed*(state: SaleSlotReserving, request: StorageRequest): ?State =
|
||||||
return some State(SaleFailed())
|
return some State(SaleFailed())
|
||||||
|
|
||||||
method onSlotFilled*(state: SaleSlotReserving, requestId: RequestId,
|
|
||||||
slotIndex: UInt256): ?State =
|
|
||||||
return some State(SaleFilled())
|
|
||||||
|
|
||||||
method run*(state: SaleSlotReserving, machine: Machine): Future[?State] {.async.} =
|
method run*(state: SaleSlotReserving, machine: Machine): Future[?State] {.async.} =
|
||||||
let agent = SalesAgent(machine)
|
let agent = SalesAgent(machine)
|
||||||
let data = agent.data
|
let data = agent.data
|
||||||
|
@ -48,7 +44,12 @@ method run*(state: SaleSlotReserving, machine: Machine): Future[?State] {.async.
|
||||||
trace "Reserving slot"
|
trace "Reserving slot"
|
||||||
await market.reserveSlot(data.requestId, data.slotIndex)
|
await market.reserveSlot(data.requestId, data.slotIndex)
|
||||||
except MarketError as e:
|
except MarketError as e:
|
||||||
return some State( SaleErrored(error: e) )
|
if e.msg.contains "Reservation not allowed":
|
||||||
|
debug "Slot cannot be reserved, ignoring", error = e.msg
|
||||||
|
return some State( SaleIgnored(reprocessSlot: false, returnBytes: true) )
|
||||||
|
else:
|
||||||
|
return some State( SaleErrored(error: e) )
|
||||||
|
# other CatchableErrors are handled "automatically" by the ErrorHandlingState
|
||||||
|
|
||||||
trace "Slot successfully reserved"
|
trace "Slot successfully reserved"
|
||||||
return some State( SaleDownloading() )
|
return some State( SaleDownloading() )
|
||||||
|
|
|
@ -75,7 +75,7 @@ proc scheduler(machine: Machine) {.async.} =
|
||||||
await running.cancelAndWait()
|
await running.cancelAndWait()
|
||||||
let fromState = if machine.state.isNil: "<none>" else: $machine.state
|
let fromState = if machine.state.isNil: "<none>" else: $machine.state
|
||||||
machine.state = next
|
machine.state = next
|
||||||
debug "enter state", state = machine.state, fromState
|
debug "enter state", state = fromState & " => " & $machine.state
|
||||||
running = machine.run(machine.state)
|
running = machine.run(machine.state)
|
||||||
running
|
running
|
||||||
.track(machine)
|
.track(machine)
|
||||||
|
|
|
@ -24,7 +24,3 @@ checksuite "sales state 'filling'":
|
||||||
test "switches to failed state when request fails":
|
test "switches to failed state when request fails":
|
||||||
let next = state.onFailed(request)
|
let next = state.onFailed(request)
|
||||||
check !next of SaleFailed
|
check !next of SaleFailed
|
||||||
|
|
||||||
test "switches to filled state when slot is filled":
|
|
||||||
let next = state.onSlotFilled(request.id, slotIndex)
|
|
||||||
check !next of SaleFilled
|
|
||||||
|
|
|
@ -51,10 +51,6 @@ asyncchecksuite "sales state 'SlotReserving'":
|
||||||
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":
|
|
||||||
let next = state.onSlotFilled(request.id, slotIndex)
|
|
||||||
check !next of SaleFilled
|
|
||||||
|
|
||||||
test "run switches to downloading when slot successfully reserved":
|
test "run switches to downloading when slot successfully reserved":
|
||||||
let next = await state.run(agent)
|
let next = await state.run(agent)
|
||||||
check !next of SaleDownloading
|
check !next of SaleDownloading
|
||||||
|
|
|
@ -115,7 +115,7 @@ method onOutputLineCaptured(node: HardhatProcess, line: string) =
|
||||||
return
|
return
|
||||||
|
|
||||||
if error =? logFile.writeFile(line & "\n").errorOption:
|
if error =? logFile.writeFile(line & "\n").errorOption:
|
||||||
error "failed to write to hardhat file", errorCode = error
|
error "failed to write to hardhat file", errorCode = $error
|
||||||
discard logFile.closeFile()
|
discard logFile.closeFile()
|
||||||
node.logFile = none IoHandle
|
node.logFile = none IoHandle
|
||||||
|
|
||||||
|
|
|
@ -128,7 +128,7 @@ method stop*(node: NodeProcess) {.base, async.} =
|
||||||
try:
|
try:
|
||||||
trace "terminating node process..."
|
trace "terminating node process..."
|
||||||
if errCode =? node.process.terminate().errorOption:
|
if errCode =? node.process.terminate().errorOption:
|
||||||
error "failed to terminate process", errCode
|
error "failed to terminate process", errCode = $errCode
|
||||||
|
|
||||||
trace "waiting for node process to exit"
|
trace "waiting for node process to exit"
|
||||||
let exitCode = await node.process.waitForExit(3.seconds)
|
let exitCode = await node.process.waitForExit(3.seconds)
|
||||||
|
|
Loading…
Reference in New Issue