Продолжения (Continuations)
Продолжения (continuations) — это мощная концепция TVM, позволяющая реализовать сложное управление потоком выполнения: от условных переходов до обработки исключений. Аналогия из реального мира — закладки в книге: вы можете сохранить текущее место (создать continuation), перейти к другой главе, а потом вернуться. Понимание continuations необходимо для чтения низкоуровневого кода и оптимизации газа в FunC/Tolk.
Continuation — уникальная концепция TVM, отсутствующая в EVM. Это “замороженное” вычисление: фрагмент кода вместе с контекстом, который можно сохранить, передать и выполнить позже.
Что такое Continuation?
Continuation в TVM — это значение на стеке, содержащее:
- Код (code) — последовательность TVM-инструкций для выполнения
- Стек (stack) — сохранённое состояние стека (опционально)
- Registers — сохранённые управляющие регистры
По сути, continuation — это функция первого класса на уровне виртуальной машины.
Обычная инструкция: ADD → выполняется сразу
Continuation: { ADD MUL } → "заморозка", выполнится позже
Типы Continuations
Ordinary Continuation (обычное продолжение)
Содержит код для последовательного выполнения. Создаётся при компиляции if/else, циклов.
// Tact код:
if (x > 0) {
y = x * 2;
}
// TVM: создаёт 2 continuation
// cont_true: { PUSH s0; PUSHINT 2; MUL; POP y }
// cont_false: { NOP }
// IFELSE — выбирает и выполняет один из них
Exceptional Continuation (продолжение для ошибок)
Используется для обработки исключений (try/catch в Tact):
// Tact код:
try {
let result = riskyOperation();
} catch (e) {
// обработка ошибки
}
// TVM: устанавливает exceptional continuation
// c2 register ← { код обработки ошибки }
// Если THROW — TVM переключается на c2
Continuations для управления потоком
Условное выполнение
TVM не имеет JUMP и JUMPI как EVM. Вместо этого используются continuations:
// EVM (Ethereum):
PUSH label
JUMPI ← прыгает к метке в коде
// TVM (TON):
PUSHCONT { ... } ← создаёт continuation
PUSHCONT { ... } ← создаёт continuation
IFELSE ← выбирает и выполняет
Преимущество: нет произвольных прыжков в коде. Каждый “прыжок” — это выполнение continuation, что делает код более структурированным и безопасным.
Ethereum EVM не имеет продолжений — управление потоком реализовано через JUMP/JUMPI (переходы по меткам в байткоде). Это более низкоуровневый подход, подверженный ошибкам (например, jump to invalid destination). TVM с continuations обеспечивает структурированное управление потоком.
Циклы
Циклы в TVM реализованы через continuations:
// REPEAT N — выполняет continuation N раз
PUSHINT 10
PUSHCONT {
// тело цикла
INC
}
REPEAT
// WHILE — выполняет пока условие истинно
PUSHCONT {
// условие: оставляет TRUE/FALSE на стеке
PUSH s0
PUSHINT 100
LESS
}
PUSHCONT {
// тело цикла
INC
}
WHILE
// UNTIL — выполняет до тех пор, пока не TRUE
PUSHCONT {
// тело + условие выхода
INC
DUP
PUSHINT 100
EQUAL
}
UNTIL
Управляющие регистры
TVM использует специальные регистры для хранения continuations:
| Регистр | Назначение |
|---|---|
| c0 | Continuation для возврата (return address) |
| c1 | Альтернативное продолжение (для некоторых инструкций) |
| c2 | Exceptional continuation (обработка ошибок) |
| c3 | Текущий код контракта |
| c4 | Persistent data (storage контракта) |
| c5 | Очередь исходящих сообщений (actions) |
| c7 | Временные данные и контекст (время, адрес, баланс) |
Когда функция вызывается, текущий continuation сохраняется в c0 (адрес возврата). При завершении TVM выполняет c0, возвращаясь к вызывающему коду.
Continuations и Exception Handling
Механизм исключений в TVM:
// TRY — устанавливает exceptional continuation
PUSHCONT {
// обработчик ошибки (catch block)
// на стеке: error_code, остаток стека
}
SETCONT c2 // c2 ← обработчик
// ... код, который может бросить исключение ...
PUSHINT 0
THROWIF 100 // если s0 != 0, бросить исключение 100
// При THROW:
// 1. TVM останавливает текущее выполнение
// 2. Помещает error_code на стек
// 3. Переключается на continuation из c2
Стандартные коды ошибок TVM:
| Код | Описание |
|---|---|
| 0 | Успех |
| 2 | Stack underflow |
| 3 | Stack overflow |
| 4 | Integer overflow |
| 5 | Range check error |
| 11 | Неизвестный opcode |
| 13 | Out of gas |
Практическое значение
Хотя разработчики на Tact и FunC не работают с continuations напрямую, понимание концепции помогает:
- Отлаживать ошибки — stacktrace TVM показывает continuations
- Оптимизировать gas — циклы с REPEAT дешевле, чем рекурсивные вызовы
- Понимать ограничения — максимальная глубина continuation stack влияет на сложность кода
- Читать FunC-код — FunC использует
ifnot,repeat,whileкоторые компилируются в continuation-инструкции
Частые ошибки
- Путают continuations с обычными функциями: continuation это замороженное состояние вычисления, включающее код и текущую позицию, а не просто вызов функции.
- Создают глубоко вложенные цепочки continuations, что приводит к избыточному расходу газа и сложности отладки.
- Не используют CATCH/TRY для обработки исключений, хотя они реализованы именно через continuations в TVM.
- Забывают, что каждый continuation занимает память (ячейку), и избыточное создание continuations увеличивает газовую стоимость.
Проверка знанийКак TVM реализует условное выполнение (if/else) без инструкций JUMP/JUMPI, которые есть в EVM?
Проверьте понимание
Закончили урок?
Отметьте его как пройденный, чтобы отслеживать свой прогресс
Войдите чтобы оценить урок