Коллатор

Менеджер коллации

Компонент, который по событиям от коллаторов, сети и верификации выбирает следующий шаг коллации, ведёт кэш блоков и коммитит подтверждённый мастер-блок.

Менеджер коллации — компонент узла, который оркестрирует выпуск блоков рабочей цепочки и мастерчейна. Сам он блоки не собирает: этим занимаются коллаторы, каждый для своей цепочки.

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

Цикл событий

Ядро менеджера — цикл с одним блоком select! на пять источников событий:

  1. фоновая задача обработки событий применённых блоков — в цикле она только проверяется на ошибку, а её падение с ошибкой останавливает весь цикл;
  2. пачка блоков, применённых узлом из сети;
  3. завершившиеся задачи коллаторов шардов и мастерчейна;
  4. завершившаяся задача отмены идущей коллации;
  5. завершившиеся задачи верификации мастер-блоков (сбор их подписей).

Цикл однопоточный по своей сути: сама коллация, верификация и отмена коллации выполняются в отдельных задачах, а менеджер лишь порождает их и разбирает результаты. Таймеров в цикле нет: менеджер реагирует только на события, поэтому «время» в его решениях всегда время цепочки из якорей, а не системные часы.

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

Якоря менеджер не запрашивает сам. Адаптер мемпула он лишь передаёт коллаторам при их создании, а сам обращается к мемпулу только с исходящими уведомлениями — их три вида: о подтверждённом мастер-блоке, об обновлении состояния мастерчейна и о верхнем обработанном якоре.

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

Результаты коллации

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

Skipped означает, что блок в этом шаге не собран. Менеджер запускает выбор следующего шага коллации, получив от коллатора время цепочки, причину форсирования мастер-блока и текущие интервалы выпуска мастер-блока. Импорт очередного якоря при этом происходит не в каждой ветке пропуска. Если мастер-коллатор пропустил сборку из-за накопленных необработанных сообщений и принудительный импорт одного якоря не запрошен, якорь намеренно не импортируется: коллатор и так хочет собрать следующий мастер-блок, а переданное менеджеру время цепочки в этом случае берётся не из кэша якорей, а от предыдущего собранного блока. Если коллатор слишком отстал по обработке якорей (мемпул может быть на паузе), якорь тоже не импортируется, и менеджеру передаётся время цепочки ранее импортированного якоря. В остальных случаях, включая успешный импорт якоря мастер-коллатором и пропуск сборки блока шарда, время цепочки, которое видит менеджер, совпадает со временем последнего импортированного якоря.

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

Cancelled в главном цикле недопустим: результат с этим статусом разбирает не он, а отдельная задача отмены коллации.

BlockCandidate означает, что кандидат собран.

Обработка кандидата блока

Когда коллатор завершает попытку кандидатом, менеджер разбирает его по шагам.

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

Если сессия совпала, кандидат кладётся в кэш блоков вместе со списком топ-блоков шардов и верхним обработанным якорем. Эти два поля заполняются только для мастер-блока, из данных мастерчейна.

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

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

Для блока шарда менеджер сразу выбирает следующий шаг коллации по времени цепочки кандидата, причине форсирования мастер-блока и текущим интервалам выпуска мастер-блока. В событие якоря шарда дополнительно попадает seqno предыдущего мастер-блока и признак «в блоке обработаны внешние сообщения»: они пригодятся при накоплении данных для решения о мастер-блоке.

Кэш блоков коллации

Кэш блоков коллации хранит недавние блоки менеджера отдельно по мастерчейну и по каждому шарду: у мастерчейна одна группа, у шардов — по группе на шард. Внутри группы блоки лежат в дереве по seqno, упорядоченные и адресуемые ровно одной записью на номер. У каждой группы есть также верхняя граница «известного синхронизированным» блока, по которой отбрасываются блоки из сети, не новее уже известного, и своя порция данных: у мастерчейна это очередь идентификаторов последних мастер-блоков, собранных самим узлом, и диапазон мастер-блоков, применённых из сети, а у шарда это пара полей балансового итога блока и накопленных сборов с эмиссией. Балансовый итог у шарда не накапливается, а перезаписывается на каждом собранном блоке шарда: в мастер-блок в итоге уходит снимок последнего блока шарда, а не сумма по всем блокам с прошлого мастер-блока. Сборы и эмиссия, наоборот, накапливаются по шарду с прошлого мастер-блока.

Внутри группы запись о блоке — собственный кандидат или блок, принятый из сети. Собственный кандидат: собранный самим узлом блок вместе с подписями верификации и статусом кандидата, а также признаком, что тот же блок позже пришёл из сети. Блок, принятый из сети, несёт состояние шарда (только для мастер-блока), диф очереди сообщений, исходящие сообщения блока, позиции обработки и признак, что тот же блок позже собрал сам узел. Общая часть записи: идентификатор блока, идентификаторы предыдущих блоков, список топ-блоков шардов (для мастер-блока), верхний обработанный якорь и seqno мастер-блока, в подграф которого блок входит.

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

Расхождение блока — противоположная ситуация: на одном и том же seqno оказываются два разных блока. Если из сети пришёл блок, не совпадающий с уже лежащим собственным кандидатом, запись в кэше заменяется на пришедшую из сети, а кэш дополнительно вычищает из очереди последних собранных мастер-блоков всё начиная с seqno расхождения. Для блока шарда отсчёт идёт от seqno его мастер-блока. Если, наоборот, собственный кандидат не совпал с уже принятым из сети блоком, запись остаётся прежней, а кандидат просто отбрасывается. В обоих случаях менеджер увеличивает счётчик расхождений и сбрасывает незакоммиченную зону внутренней очереди сообщений: работа, которую узел успел проделать сам, признаётся некорректной. Если синхронизироваться по другим причинам не требуется, менеджер всё равно запрашивает отмену всех идущих коллаций.

Из кэша менеджер извлекает не отдельные блоки, а подграф целого мастер-блока — он и уходит на коммит. Обход начинается с топ-блоков шардов мастер-блока, у которых шард действительно продвинулся с прошлого мастер-блока, и дальше идёт рекурсивно по ссылкам на предыдущие блоки каждого найденного блока. Блок входит в подграф, только если его seqno мастер-блока совпадает с seqno извлекаемого: так обход естественно останавливается на топ-блоках предыдущего мастер-блока. Каждая найденная запись удаляется из кэша, а верхняя граница «известного синхронизированным» у соответствующего шарда подтягивается до её seqno. Извлечение всегда деструктивно и происходит один раз: запись мастер-блока изымается из кэша ещё до обхода, и повторное извлечение того же блока ничего не находит.

Выбор следующего шага коллации

Каждый раз, когда у менеджера появляется новое событие импорта якоря (от пропущенного шага или от собранного кандидата), он дописывает его в состояние соответствующего шарда: время цепочки, признак, что этот шард явно форсировал мастер-блок, и, если шард только что собрал блок, seqno предыдущего мастер-блока с признаком того, были ли в блоке обработаны внешние сообщения. По накопленным событиям для каждого активного шарда менеджер считает сводку: время первого события, первое время, на котором шард форсировал мастер-блок, первое время, отстоящее от времени последнего мастер-блока не меньше чем на минимальный интервал, первое время, отстоящее от него не меньше чем на максимальный интервал, и признак того, был ли после прошлого мастер-блока собран блок шарда с обработанными внешними сообщениями.

Мастер-блок менеджер собирает только тогда, когда кандидата на него дали все активные шарды, включая сам мастерчейн: пока хотя бы один не готов, решение о мастер-блоке не принимается. Время цепочки нового мастер-блока — это максимум по кандидатам всех шардов, а не минимум: так мастер-блок покрывает события всех шардов, а не только самого раннего. Приняв это решение, менеджер переводит все шарды в состояние «попытки идут» и у мастерчейна отбрасывает накопленные события с временем цепочки выше выбранного (такие якоря будут импортированы заново). Дальше он монотонно поднимает время последнего мастер-блока до выбранного, если оно ещё меньше, и вычищает у всех шардов события не позже него. Ещё он сбрасывает оба признака форсирования мастер-блока: они держатся не на каждом шаге, а именно до этого момента.

Если хотя бы один активный шард кандидата не дал, менеджер вместо сборки мастер-блока раздаёт статусы ожидания и решает, какие шарды возобновить. Решение о следующем шаге («шаг коллации») имеет ровно четыре варианта:

  • CollateMaster запускает коллацию мастер-блока на выбранное время цепочки. Коллатор шарда, чьё событие вызвало это решение, переводится в ожидание.
  • ResumeAttemptsIn для каждого перечисленного в нём шарда запускает очередную попытку: для мастерчейна это импорт следующего якоря, для рабочей цепочки — попытка собрать следующий блок. Если в списке нет текущего шарда (того, чьё событие обрабатывается), его коллатор переводится в ожидание.
  • WaitForMasterStatus и WaitForShardStatus просто переводят текущий коллатор в ожидание, не порождая новых задач.

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

  • коллатор шарда без кандидата, если состояние мастерчейна ещё не заведено, переходит в ожидание мастера;
  • коллатор мастерчейна, если хотя бы один шард ещё не готов, переходит в ожидание шардов;
  • в остальных случаях коллатор переходит в состояние «попытки идут» и попадает в список возобновляемых;
  • шард, только что давший кандидата, получает статус «готов собирать мастер-блок».

Дальше идёт «побудка»: событие от мастерчейна переводит всех, кто ждал статуса мастера, в состояние «попытки идут» и добавляет их в список возобновляемых, а событие от шарда так же будит всех, кто ждал статуса шардов. Если будить некого, результатом становится противоположное ожидание: ожидание шардов, если событие пришло от мастерчейна, и ожидание мастера, если оно пришло от шарда.

Пять причин форсирования мастер-блока

Чтобы решить, дал ли шард кандидата на мастер-блок и на какое время цепочки, менеджер для каждого активного шарда проверяет пять причин по порядку и берёт результат первой сработавшей.

  • Шард сам форсировал мастер-блок. Время берётся с момента, когда коллатор этого шарда впервые вернул форсирующую причину.
  • Мастерчейн форсировал сборку для всех. Это происходит, когда необработанные сообщения остались у самого мастерчейна. Время берётся с самого раннего накопленного момента по шарду.
  • Прошёл минимальный интервал, и после последнего мастер-блока хотя бы один шард выпустил блок с обработанными внешними сообщениями. Причина засчитывается только для шарда, уже готового участвовать в решении: самого текущего шарда либо шарда в одном из статусов готовности или ожидания.
  • Прошёл минимальный интервал, и хотя бы один шард сообщил, что после его блоков не осталось ожидающих сообщений. Условие готовности то же, что в предыдущем пункте, а временем становится более позднее из двух: момент самого интервала или момент этого сообщения — так мастер-блок не назначается раньше, чем оно сработало.
  • Прошёл максимальный интервал. Срабатывает, только если ни одно из двух предыдущих условий не выполнено.

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

Причины 1 и 2 (и мягкая причина 4) приходят от коллаторов в виде одного из четырёх сигналов форсирования, которые они возвращают менеджеру: коллатор шарда упёрся в лимит длины незакоммиченной цепочки (max_uncommitted_chain_length в конфигурации коллации), импорт следующего якоря пришлось пропустить, из-за чего время цепочки не двигается и интервал сам по себе никогда не истечёт, у коллатора остались необработанные сообщения из предыдущего блока, или после уже собранного блока шарда не осталось ожидающих сообщений. Первые три из них менеджер считает жёстким форсированием, четвёртый — мягким. Сигнал «необработанные сообщения», пришедший от мастерчейна, менеджер превращает в отдельный флаг: форсирование сборки для всех шардов сразу. Это и есть причина 2.

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

Запуск коллации мастер-блока

Решив собирать мастер-блок, менеджер забирает коллатор мастерчейна из реестра активных коллаторов и переводит его в Active, но только если тот уже «готов» — находится в состоянии Active или Waiting. Исходов три:

  • коллатор изъят, коллация запускается;
  • проверка состояния не прошла (коллатор отменён или отмена ещё идёт) — менеджер логирует, что коллатор мастерчейна не готов, и ничего не запускает;
  • записи о коллаторе мастерчейна в реестре нет, либо он уже занят другой задачей — менеджер возвращает ошибку, которая уходит в главный цикл: занятость и отсутствие коллатора здесь не пропускаются тихо.

Список топ-блоков шардов для нового мастер-блока менеджер собирает из кэша: по каждому шарду он проходит записи от старших seqno к младшим и берёт первый блок, чей seqno мастер-блока совпадает с seqno собираемого и у которого есть дополнительная информация кэша. У собственного блока эта информация есть всегда, у блока, принятого из сети, она есть, только если тот же блок был позже собран и локально. Если её нет, поиск продолжается к более старым блокам. Вместе с блоком в описание топ-блока уходят его данные, идентификатор обработанного якоря, позиции обработки внутренних сообщений по партициям, а также балансовый итог блока и накопленные сборы с эмиссией из данных группы кэша — оба поля при этом обнуляются в кэше. У этих двух полей разная семантика: сборы и эмиссия накапливались по шарду с прошлого мастер-блока, поэтому в мастер-блок уходит их сумма, а балансовый итог не накапливался, а перезаписывался на каждом собранном блоке шарда, поэтому уходит снимок последнего блока, а не сумма.

Задача коллации мастер-блока запускается как отдельная фоновая задача с этими топ-блоками шардов и выбранным временем цепочки на входе.

Верификация и коммит мастер-блока

Верифицируются в Tycho только мастер-блоки. Менеджер запускает задачу верификации исключительно для кандидата мастер-блока, и только если этот же блок ещё не пришёл из сети. Для кандидата блока шарда верификация не запускается вовсе — менеджер сразу переходит к выбору следующего шага коллации. Сессии верификации менеджер тоже заводит только для мастерчейна: передаёт верификатору шард, идентификатор сессии верификации (пара «catchain seqno» и раунд смены набора валидаторов), seqno первого блока сессии, хеш набора валидаторов и список участников подмножества. Блок рабочей цепочки становится подтверждённым не сам по себе, а опосредованно, попадая в подграф мастер-блока, для которого подписи как раз и собираются. Отдельного механизма подтверждения для рабочих цепочек в Tycho нет.

У кандидата мастер-блока четыре статуса:

  • Collated — блок собран.
  • Validated — блок подтверждён подписями.
  • ValidationSkipped — верификация пропущена.
  • Synced — этот же блок уже пришёл из сети.

Результат верификации переводит кандидата в статус «подтверждён», сохраняя подписи и их суммарный вес, либо в статус «верификация пропущена», без подписей и с нулевым весом. Применяется этот результат только к записи в статусе «собран»: если блока уже нет в кэше, если запись уже в статусе «уже пришёл из сети» или если статус не изменился, обновление отбрасывается. Не применяется он и к блокам, принятым из сети целиком: их собственный результат верификации уже не нужен. Коммитит мастер-блок менеджер только при новом статусе «подтверждён». На пропущенную верификацию и любой другой статус коммит не запускается.

Если тот же блок уже пришёл из сети до завершения верификации, менеджер вообще не дожидается её результата: как только кандидат и принятый блок совпали в кэше, статус кандидата сразу становится «уже пришёл из сети», и менеджер переходит прямо к коммиту, минуя запуск верификации.

Коммит подтверждённого мастер-блока начинается с извлечения его подграфа из кэша, а дальше действует по виду записи.

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

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

Коммит внутренней очереди менеджер запускает только после того, как мастер-блок действительно применён в состоянии узла: он подписывается на применённый мастер-блок и ждёт, пока его seqno не станет больше коммитимого или не совпадёт с ним при совпадении идентификаторов. Совпадение seqno при разных идентификаторах блока — ошибка: значит, менеджер пытается закоммитить очередь по неподтверждённому или неверному мастер-блоку. Такое ожидание может занять сколь угодно долго. Это осознанное решение: коммит очереди до фиксации блока нарушил бы согласованность.

Коммит внутренней очереди запускается из трёх точек менеджера:

  • по событию о применении собственного блока: если применённый блок принадлежит мастерчейну, менеджер коммитит очередь с топ-блоками шардов из состояния этого блока и партициями, пришедшими вместе с событием;
  • при коммите мастер-блока, уже принятого из сети целиком: событие о применении собственного блока по нему не придёт, поэтому очередь коммитится сразу, партициями из извлечённого подграфа;
  • при восстановлении очереди во время синхронизации узла на применённый мастер-блок.

С применением собственного блока связано ещё одно действие: менеджер планирует отмену сессий верификации до этого блока. Триггер один и тот же для применения своего и полученного из сети мастер-блока, а для блока шарда планирование сразу выходит — отменять нечего. Для мастер-блока менеджер снимает идентификатор текущей сессии верификации мастерчейна и ставит задачу отмены в очередь: если отмена уже идёт, новый запрос заменяет ранее отложенный, а не встаёт следом. Отмена «до блока» монотонна, поэтому последний по времени запрос и перекрывает все предыдущие. Отменяет её менеджер простым сообщением верификатору о том, что подписи по блокам до указанного собирать больше не нужно. Ошибка отмены только логируется и работу менеджера не прерывает.

Отмена коллации

Менеджер запускает отмену коллации, чтобы прервать уже идущую сборку блоков и перейти к другому действию, а не позволить ей завершиться самой.

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

У отмены всегда есть действие «после»: либо ничего не делать, либо синхронизироваться на применённый мастер-блок с сохранёнными параметрами (тем блоком, что её спровоцировал, последним собственным мастер-блоком, диапазоном применённых из сети и режимом обработки состояния мастерчейна). Новая отмена не запускается, только если действия нет вовсе — ни нового, ни уже назначенного. Если действие уже назначено предыдущим вызовом, отмена продолжает работать с ним и без нового действия (см. выше). Пока предыдущая отмена ещё идёт, действие можно обновить, но по правилу приоритета: заменить уже назначенную синхронизацию на «ничего не делать» нельзя, а любые другие замены допускаются. Так менеджер не теряет необходимость синхронизироваться, если в одной пачке блоков из сети встретились сразу и расхождение блока, и признак сильного отставания. Завершившаяся задача отмены отдаёт накопленное действие обратно в главный цикл, а он либо запускает синхронизацию, либо ничего не делает.