# beacon_chain # Copyright (c) 2021-2024 Status Research & Development GmbH # Licensed and distributed under either of # * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). # at your option. This file may not be copied, modified, or distributed except according to those terms. {.push raises: [].} import std/algorithm import chronicles import "."/[common, api] const ServiceName = "fork_service" logScope: service = ServiceName proc validateForkSchedule(forks: openArray[Fork]): bool {.raises: [].} = # Check if `forks` list is linked list. var current_version = forks[0].current_version for index, item in forks: if index > 0: if item.previous_version != current_version: return false else: if item.previous_version != item.current_version: return false current_version = item.current_version true proc sortForks(forks: openArray[Fork]): Result[seq[Fork], cstring] {. raises: [].} = proc cmp(x, y: Fork): int {.closure.} = if uint64(x.epoch) == uint64(y.epoch): return 0 if uint64(x.epoch) < uint64(y.epoch): return -1 return 1 if len(forks) == 0: return err("Empty fork schedule") let sortedForks = sorted(forks, cmp) if not(validateForkSchedule(sortedForks)): return err("Invalid fork schedule") ok(sortedForks) proc pollForFork(vc: ValidatorClientRef) {.async: (raises: [CancelledError]).} = let forks = try: await vc.getForkSchedule() except ValidatorApiError as exc: warn "Unable to retrieve fork schedule", reason = exc.getFailureReason(), err_msg = exc.msg return except CancelledError as exc: debug "Fork retrieval process was interrupted" raise exc if len(forks) == 0: return let sortedForks = block: let res = sortForks(forks) if res.isErr(): warn "Invalid fork schedule received", reason = res.error() return res.get() if (len(vc.forks) == 0) or (vc.forks != sortedForks): vc.forks = sortedForks vc.updateForkConfig() notice "Fork schedule updated", fork_schedule = sortedForks vc.forksAvailable.fire() proc mainLoop(service: ForkServiceRef) {.async: (raises: []).} = let vc = service.client service.state = ServiceState.Running debug "Service started" try: await vc.preGenesisEvent.wait() except CancelledError: debug "Service interrupted" return while true: # This loop could look much more nicer/better, when # https://github.com/nim-lang/Nim/issues/19911 will be fixed, so it could # become safe to combine loops, breaks and exception handlers. let breakLoop = try: await vc.pollForFork() await service.waitForNextEpoch(TIME_DELAY_FROM_SLOT) false except CancelledError: debug "Service interrupted" true if breakLoop: break proc init*( t: typedesc[ForkServiceRef], vc: ValidatorClientRef ): Future[ForkServiceRef] {.async: (raises: []).} = logScope: service = ServiceName let res = ForkServiceRef(name: ServiceName, client: vc, state: ServiceState.Initialized) debug "Initializing service" res proc start*(service: ForkServiceRef) = service.lifeFut = mainLoop(service)