> ## Documentation Index
> Fetch the complete documentation index at: https://docs.intention.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Программные сервисы

> Сервисы, которые офчейн выводят состояние счёта из зафиксированной истории торгов и записывают результат обратно транзакциями протокола.

Часть того, что площадке нужно знать о счёте, невозможно посчитать во время исполнения блока. Уровень по объёму зависит от тридцати дней торгов. Вознаграждение зависит от окна, которое ещё не закрылось. Реферальная привязка зависит от связи, установленной месяцы назад.

Затащить эту работу внутрь исполнения блока было бы ошибкой сразу по двум причинам: каждый блок платил бы за расчёт, который почти ни одному блоку не нужен, а ядру пришлось бы нести историю, которую ему больше незачем хранить.

Программные сервисы решают это, разворачивая направление. Расчёт идёт **вне** блока, поверх зафиксированной записи. Его *результат* затем фиксируется **обратно** в блокчейне как состояние протокола, откуда исполнение читает его за константное время, как любую другую конфигурацию.

<div className="dg" data-dg="program-writeback">
  <div className="dg-c" style={{aspectRatio:"720 / 348"}}>
    <svg className="dg-w" viewBox="0 0 720 348" aria-hidden="true">
      <path className="dg-wire" d="M 144.00 70.00 L 169.60 70.00" />

      <path className="dg-head" d="M 176.00 70.00 L 169.60 74.40 L 169.60 65.60 Z" />

      <path className="dg-wire dg--sky" d="M 344.00 70.00 L 369.60 70.00" />

      <path className="dg-head dg--sky" d="M 376.00 70.00 L 369.60 74.40 L 369.60 65.60 Z" />

      <path className="dg-wire" d="M 534.00 70.00 L 561.75 38.78" />

      <path className="dg-head" d="M 566.00 34.00 L 565.04 41.71 L 558.46 35.86 Z" />

      <path className="dg-wire dg--blue" d="M 534.00 70.00 L 561.33 95.62" />

      <path className="dg-head dg--blue" d="M 566.00 100.00 L 558.32 98.83 L 564.34 92.41 Z" />

      <path className="dg-wire dg--blue" d="M 645.00 128.00 L 645.00 136.00 L 465.00 136.00 L 465.00 142.00" />

      <path className="dg-head dg--blue" d="M 465.00 142.00 L 460.60 135.60 L 469.40 135.60 Z" />

      <path className="dg-wire dg--green" d="M 554.00 181.00 L 567.60 181.00" />

      <path className="dg-head dg--green" d="M 574.00 181.00 L 567.60 185.40 L 567.60 176.60 Z" />
    </svg>

    <div className="dg-b" style={{left:"0.0000%",top:"11.4943%",width:"19.4444%",height:"17.2414%"}}><span className="dg-t">Зафиксированная история торгов</span></div>
    <div className="dg-b dg--sky" style={{left:"25.0000%",top:"5.7471%",width:"22.2222%",height:"28.7356%"}}><span className="dg-t">Программный сервис</span><span className="dg-s">расчёт по окну, вне блока</span><span className="dg-n">читает таблицу комиссий из блокчейна</span></div>
    <div className="dg-b dg--yellow dg-round" style={{left:"52.7778%",top:"10.3448%",width:"20.8333%",height:"19.5402%"}}><span className="dg-t">Изменилось с прошлого применения?</span></div>
    <div className="dg-b" style={{left:"79.1667%",top:"2.8736%",width:"20.8333%",height:"13.7931%"}}><span className="dg-t">Ничего не пишется</span></div>
    <div className="dg-b dg--blue" style={{left:"79.1667%",top:"21.8391%",width:"20.8333%",height:"13.7931%"}}><span className="dg-t">Транзакция протокола</span></div>
    <div className="dg-b dg--green" style={{left:"52.7778%",top:"41.9540%",width:"23.6111%",height:"20.1149%"}}><span className="dg-t">Состояние блокчейна</span></div>
    <div className="dg-b dg--green" style={{left:"80.2778%",top:"41.9540%",width:"19.7222%",height:"20.1149%"}}><span className="dg-t">Чтение при исполнении</span><span className="dg-s">за константное время</span></div>
    <div className="dg-b dg-dashed dg-left" style={{left:"0.0000%",top:"70.1149%",width:"48.8889%",height:"24.1379%"}}><span className="dg-t">Сравнение по ставкам, а не по позициям</span><span className="dg-s">Сдвиг порога или переоценка уровня меняют то, сколько счёт платит, не меняя индекса его уровня.</span></div>
    <div className="dg-b dg-dashed dg-left" style={{left:"51.1111%",top:"70.1149%",width:"48.8889%",height:"24.1379%"}}><span className="dg-t">Итоговую ставку определяет блокчейн</span><span className="dg-s">Период отмечается применённым только после фиксации каждого пакета; падение приводит к повтору всего периода — это безопасно, потому что расчёт идемпотентный.</span></div>
  </div>
</div>

Источником истины остаётся блокчейн. Сервис не хранит состояние, от которого зависит сеть, — он предлагает значение, и реально только то, что блокчейн принял.

<h2 id="what-runs-today">
  Что работает сегодня
</h2>

**Комиссионные уровни по объёму.** Сервис принимает историю торгов, которую передаёт узел, накапливает объём по каждому счёту и делает снимки по расписанию. Когда период закрывается, сервис считает объём каждого счёта за скользящее окно, переводит его в ставку по конфигурации комиссий, **прочитанной с блокчейна вживую**, и пакетами пишет обратно те счета, у которых что-то изменилось.

Несколько деталей в этой фразе несущие:

* **Таблица уровней читается с блокчейна и никогда не зашивается в код.** Сервис со своей копией продолжал бы применять вчерашнюю сетку после того, как сеть её изменила.
* **Сравнение идёт по ставкам, а не по позициям в таблице уровней.** Сравнение *индексов* уровня упускает два реальных случая: порог сдвинулся — и счёт, который сам не менялся, оказался на другом уровне; уровень переоценили — а его индекс остался прежним. И то и другое меняет то, сколько счёт платит, и ни то ни другое не меняет индекс.
* **Итоговую ставку определяет блокчейн.** Транзакция несёт индекс уровня, а итоговую ставку исполнение берёт из актуальной конфигурации комиссий. Если индекс выходит за допустимый диапазон, падает весь пакет — частично он не применяется.
* **Период отмечается применённым только после того, как зафиксирован каждый пакет.** Падение в середине периода приводит к пересчёту всего периода — это безопасно, потому что расчёт идемпотентный: то же окно даёт тот же результат.

<h2 id="the-failure-model">
  Модель отказов
</h2>

Эти сервисы стоят между двумя системами, каждая из которых иногда будет недоступна. Конструкция исходит из этого, а не считает это исключением.

Отказы зависимостей — базы данных, потока от узла, API узла — обрабатываются повторными попытками с нарастающей задержкой. Процесс от них не завершается: перезапуск не чинит недоступную зависимость, он лишь добавляет к простою холодный старт. Фатальным остаётся то, что перезапуск *починить может* или что оператор обязан увидеть: неверная конфигурация при старте, невозможность поднять эндпоинт проверки состояния и паники.

Во время сбоя процесс продолжает работать, сообщает о себе «не готов» и считает ошибки. Поэтому эксплуатационный сигнал — это «**не готов уже N минут**», а не «**процесс жив**». Полезен именно первый вопрос: живой процесс, который час не может ничего принять, — это и есть настоящий инцидент.

На сигналы оркестратора процесс завершается корректно: работа останавливается, контрольные точки сбрасываются на диск, процесс выходит чисто. Без этого каждое рутинное развёртывание стоило бы несохранённого окна и повторного расчёта.

<Note>
  Период, который посчитан, но ещё не применён, — не потерянный период. Расчёт идемпотентный, а период отмечается применённым только после успешной записи, поэтому прерванный запуск возобновляется повтором окна, а не пропускает его.
</Note>

<h2 id="why-the-pattern-generalizes">
  Почему этот паттерн обобщается
</h2>

Путь обратной записи универсален. В протоколе есть транзакции для установки конфигурации на уровне счёта и для установки глобальной конфигурации, а программный сервис — это любой процесс, который вычисляет значение для одной из них по зафиксированной истории.

Работают только комиссионные уровни. Программы стимулирования, реферальная привязка и право участия в кампаниях имели бы ту же форму: расчёт по окну поверх истории торгов, сравнение с тем, что применено сейчас, и пакетная обратная запись. Их место было бы здесь, а не в ядре, ровно по той же причине, что и у комиссионных уровней: расчёт периодический и исторический, а исполнению нужен ответ за константное время. Ничего из этого не построено; обобщается паттерн, а не обязательство, что они им воспользуются.

Коммерческие условия этих программ описаны в разделе [Комиссии и программы](/ru/programs/fees). Эта страница — о том, как результат попадает в блокчейн.

<h2 id="where-to-go-next">
  Что дальше
</h2>

<CardGroup cols={2}>
  <Card title="Индексатор" href="/ru/protocol/architecture/indexer">
    Поток, который потребляют эти сервисы.
  </Card>

  <Card title="Комиссии" href="/ru/programs/fees">
    Коммерческая сторона: какие есть уровни и сколько они стоят.
  </Card>

  <Card title="IntentionKernel" href="/ru/protocol/architecture/kernel">
    Как записанная обратно конфигурация читается во время исполнения.
  </Card>

  <Card title="Модель состояния" href="/ru/protocol/architecture/state/model">
    Почему ключи конфигурации версионируются и почему клиентам стоит читать их вживую.
  </Card>
</CardGroup>
