feat: repair is rewarded (#1022)

* feat: repair is rewarded

* chore: update contracts repo

* feat: proving loop handles repair case

* test: assert repair state

* chore: update contracts repo

* fix: upon unknown state of repair go to error
This commit is contained in:
Adam Uhlíř 2024-12-12 21:19:56 +01:00 committed by GitHub
parent d10072bf67
commit 19af79786e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 38 additions and 5 deletions

View File

@ -84,6 +84,11 @@ method proofTimeout*(market: OnChainMarket): Future[UInt256] {.async.} =
let config = await market.config() let config = await market.config()
return config.proofs.timeout return config.proofs.timeout
method repairRewardPercentage*(market: OnChainMarket): Future[uint8] {.async.} =
convertEthersError:
let config = await market.contract.configuration()
return config.collateral.repairRewardPercentage
method proofDowntime*(market: OnChainMarket): Future[uint8] {.async.} = method proofDowntime*(market: OnChainMarket): Future[uint8] {.async.} =
convertEthersError: convertEthersError:
let config = await market.config() let config = await market.config()

View File

@ -49,6 +49,7 @@ type
Failed Failed
Paid Paid
Cancelled Cancelled
Repair
proc `==`*(x, y: Nonce): bool {.borrow.} proc `==`*(x, y: Nonce): bool {.borrow.}
proc `==`*(x, y: RequestId): bool {.borrow.} proc `==`*(x, y: RequestId): bool {.borrow.}

View File

@ -67,6 +67,9 @@ method periodicity*(market: Market): Future[Periodicity] {.base, async.} =
method proofTimeout*(market: Market): Future[UInt256] {.base, async.} = method proofTimeout*(market: Market): Future[UInt256] {.base, async.} =
raiseAssert("not implemented") raiseAssert("not implemented")
method repairRewardPercentage*(market: Market): Future[uint8] {.base, async.} =
raiseAssert("not implemented")
method proofDowntime*(market: Market): Future[uint8] {.base, async.} = method proofDowntime*(market: Market): Future[uint8] {.base, async.} =
raiseAssert("not implemented") raiseAssert("not implemented")

View File

@ -1,3 +1,4 @@
import pkg/stint
import ../../logutils import ../../logutils
import ../../market import ../../market
import ../statemachine import ../statemachine
@ -27,13 +28,23 @@ method onFailed*(state: SaleFilling, request: StorageRequest): ?State =
method run(state: SaleFilling, machine: Machine): Future[?State] {.async.} = method run(state: SaleFilling, machine: Machine): Future[?State] {.async.} =
let data = SalesAgent(machine).data let data = SalesAgent(machine).data
let market = SalesAgent(machine).context.market let market = SalesAgent(machine).context.market
without (collateral =? data.request.?ask.?collateral): without (fullCollateral =? data.request.?ask.?collateral):
raiseAssert "Request not set" raiseAssert "Request not set"
logScope: logScope:
requestId = data.requestId requestId = data.requestId
slotIndex = data.slotIndex slotIndex = data.slotIndex
let slotState = await market.slotState(slotId(data.requestId, data.slotIndex))
var collateral: Uint256
if slotState == SlotState.Repair:
# When repairing the node gets "discount" on the collateral that it needs to
let repairRewardPercentage = (await market.repairRewardPercentage).u256
collateral = fullCollateral - ((fullCollateral * repairRewardPercentage)).div(100.u256)
else:
collateral = fullCollateral
debug "Filling slot" debug "Filling slot"
try: try:
await market.fillSlot(data.requestId, data.slotIndex, state.proof, collateral) await market.fillSlot(data.requestId, data.slotIndex, state.proof, collateral)

View File

@ -49,7 +49,7 @@ method run*(state: SalePreparing, machine: Machine): Future[?State] {.async.} =
let slotId = slotId(data.requestId, data.slotIndex) let slotId = slotId(data.requestId, data.slotIndex)
let state = await market.slotState(slotId) let state = await market.slotState(slotId)
if state != SlotState.Free: if state != SlotState.Free and state != SlotState.Repair:
return some State(SaleIgnored(reprocessSlot: false, returnBytes: false)) return some State(SaleIgnored(reprocessSlot: false, returnBytes: false))
# TODO: Once implemented, check to ensure the host is allowed to fill the slot, # TODO: Once implemented, check to ensure the host is allowed to fill the slot,

View File

@ -16,6 +16,7 @@ logScope:
topics = "marketplace sales proving" topics = "marketplace sales proving"
type type
SlotFreedError* = object of CatchableError
SlotNotFilledError* = object of CatchableError SlotNotFilledError* = object of CatchableError
SaleProving* = ref object of ErrorHandlingState SaleProving* = ref object of ErrorHandlingState
loop: Future[void] loop: Future[void]
@ -82,6 +83,10 @@ proc proveLoop(
of SlotState.Cancelled: of SlotState.Cancelled:
debug "Slot reached cancelled state" debug "Slot reached cancelled state"
# do nothing, let onCancelled callback take care of it # do nothing, let onCancelled callback take care of it
of SlotState.Repair:
warn "Slot was forcible freed"
let message = "Slot was forcible freed and host was removed from its hosting"
raise newException(SlotFreedError, message)
of SlotState.Failed: of SlotState.Failed:
debug "Slot reached failed state" debug "Slot reached failed state"
# do nothing, let onFailed callback take care of it # do nothing, let onFailed callback take care of it

View File

@ -5,6 +5,7 @@ import ./filled
import ./finished import ./finished
import ./failed import ./failed
import ./errored import ./errored
import ./proving
import ./cancelled import ./cancelled
import ./payout import ./payout
@ -38,7 +39,7 @@ method run*(state: SaleUnknown, machine: Machine): Future[?State] {.async.} =
case slotState case slotState
of SlotState.Free: of SlotState.Free:
let error = newException(UnexpectedSlotError, let error = newException(UnexpectedSlotError,
"slot state on chain should not be 'free'") "Slot state on chain should not be 'free'")
return some State(SaleErrored(error: error)) return some State(SaleErrored(error: error))
of SlotState.Filled: of SlotState.Filled:
return some State(SaleFilled()) return some State(SaleFilled())
@ -50,3 +51,7 @@ method run*(state: SaleUnknown, machine: Machine): Future[?State] {.async.} =
return some State(SaleFailed()) return some State(SaleFailed())
of SlotState.Cancelled: of SlotState.Cancelled:
return some State(SaleCancelled()) return some State(SaleCancelled())
of SlotState.Repair:
let error = newException(SlotFreedError,
"Slot was forcible freed and host was removed from its hosting")
return some State(SaleErrored(error: error))

View File

@ -125,6 +125,9 @@ method proofTimeout*(market: MockMarket): Future[UInt256] {.async.} =
method proofDowntime*(market: MockMarket): Future[uint8] {.async.} = method proofDowntime*(market: MockMarket): Future[uint8] {.async.} =
return market.config.proofs.downtime return market.config.proofs.downtime
method repairRewardPercentage*(market: MockMarket): Future[uint8] {.async.} =
return market.config.collateral.repairRewardPercentage
method getPointer*(market: MockMarket, slotId: SlotId): Future[uint8] {.async.} = method getPointer*(market: MockMarket, slotId: SlotId): Future[uint8] {.async.} =
return market.proofPointer return market.proofPointer

View File

@ -303,7 +303,7 @@ ethersuite "On-Chain Market":
let slotId = request.slotId(slotIndex.u256) let slotId = request.slotId(slotIndex.u256)
while true: while true:
let slotState = await marketplace.slotState(slotId) let slotState = await marketplace.slotState(slotId)
if slotState == SlotState.Free: if slotState == SlotState.Repair or slotState == SlotState.Failed:
break break
await waitUntilProofRequired(slotId) await waitUntilProofRequired(slotId)
let missingPeriod = periodicity.periodOf(await ethProvider.currentTime()) let missingPeriod = periodicity.periodOf(await ethProvider.currentTime())

@ -1 +1 @@
Subproject commit 945f6008c8817abd7ca43a40368d33bb1e014c14 Subproject commit dfab6102e71d2acaff86af45b87be2536530c624