Транзакция
В блокчейне TON любое изменение состояния аккаунта записывается с помощью транзакции. В отличие от сообщений, транзакции ни чего не перемещают, отправляют или получают. Эти термины часто путают, но важно понимать, что транзакция — это просто запись всех изменений, произошедших с конкретным аккаунтом.
В этом разделе вы узнаете, как устроена транзакция, как она проходит через каждую фазу, как получать данные о транзакциях с помощью API и как определить, завершилось ли событие в блокчейне успехом.
Структура транзакции
TL-B
Перед тем, как погружаться в принципы работы транзакций, нам нужно понять их структуру с помощью TL-B (Type Language – Binary). Стоит отметить, что в TON существует несколько типов транзакций; однако в этом руководстве мы сосредоточимся исключительно на обычной транзакции. Такие транзакции актуальны для обработки платежей и разработки большинства приложений на TON.
trans_ord$0000 credit_first:Bool
storage_ph:(Maybe TrStoragePhase)
credit_ph:(Maybe TrCreditPhase)
compute_ph:TrComputePhase action:(Maybe ^TrActionPhase)
aborted:Bool bounce:(Maybe TrBouncePhase)
destroyed:Bool
= TransactionDescr;
Согласно схеме TL-B, транзакция состоит из следующих полей:
Поле | Тип | Описание |
---|---|---|
credit_first | Bool | Указывает, должна ли фаза кредита исполняться первой. Это зависит от того, выставлен ли флаг возврата. Это будет объяснено подробнее дальше. |
storage_ph | Maybe TrStoragePhase | Фаза хранения, отвечает за обработку комиссий, связанных с постоянным хранилищем аккаунта. |
credit_ph | Maybe TrCreditPhase | Фаза кредита, отвечает за передачу средств, доставленных с входящим сообщением, если оно внутреннее (тип #1 или #2). |
compute_ph | TrComputePhase | Фаза вычислений, отвечает за исполнение кода смарт-контракта, хранящегося у аккаунта в ячейке code . |
action | Maybe ^TrActionPhase | Фаза действий, отвечает за обработку любых действий, инициированных в ходе фазе вычислений. |
aborted | Bool | Указывает, была ли транзакция прервана на одном из этапов. Если true , транзакция не была выполнена, и изменения из фаз compute_ph и action не были применены. |
bounce | Maybe TrBouncePhase | Фаза отскока, отвечающая за обработку ошибок, произошедших на этапах compute_ph или action . |
destroyed | Bool | Указывает, был ли аккаунт уничтожен во время выполнения транзакции. |
Другие типы транзакций, например, trans_storage, trans_tick_tock и trans_split_prepare, используются для внутренних событий, невидимых для конечных пользователей. К ним относятся разделение и слияние шардов, tick-tock-транзакции и т. д.
Поскольку они не имеют отношения к разработке dApp, мы не будем рассматривать их в этом руководстве.
Фаза кредита (credit phase)
Эта фаза (credit phase) относительно небольшая и простая. Если вы посмотрите исходный код блокчейна, увидите, что её основная логика заключается в пополнении баланса контракта оставшимися средствами из входящего сообщения.
credit_phase->credit = msg_balance_remaining;
if (!msg_balance_remaining.is_valid()) {
LOG(ERROR) << "cannot compute the amount to be credited in the credit phase of transaction";
return false;
}
// NB: msg_balance_remaining may be deducted from balance later during bounce phase
balance += msg_balance_remaining;
if (!balance.is_valid()) {
LOG(ERROR) << "cannot credit currency collection to account";
return false;
}
Фаза кредита сериализуется в TL-B следующим образом:
tr_phase_credit$_ due_fees_collected:(Maybe Grams)
credit:CurrencyCollection = TrCreditPhase;
Эта фаза состоит из двух полей:
Поле | Тип | Описание |
---|---|---|
due_fees_collected | Maybe Grams | Сумма взимаемых сборов за хранение. Это поле присутствует, если у аккаунта нет средств на балансе, и накопился долг за оплату хранилища. |
credit | CurrencyCollection | Сумма, зачисленная аккаунту в результате получения сообщения. |
Фаза хранения (storage phase)
В этой фазе блокчейн обрабатывает комиссии, связанные с постоянным хранилищем аккаунта. Для начала посмотрим на схему TL-B:
tr_phase_storage$_ storage_fees_collected:Grams
storage_fees_due:(Maybe Grams)
status_change:AccStatusChange
= TrStoragePhase;
Эта фаза включает следующие поля:
Поле | Тип | Описание |
---|---|---|
storage_fees_collected | Grams | Сумма комиссии за хранение данных, взимаемая с аккаунта. |
storage_fees_due | Maybe Grams | Сумма комиссии за хранение данных, которая была начислена, однако не могла быть получена из-за недостаточного баланса. Эта сумма представляет собой накопленный долг. |
status_change | AccStatusChange | Изменение статуса аккаунта после исполнения транзакции. |
Поле storage_fees_due
имеет тип Maybe
, потому что оно присутствует только тогда, когда у аккаунта недостаточный баланс для покрытия комиссии за хранение. Когда у аккаунта достаточно средств, это поле опускается.
Поле AccStatusChange
указывает, изменился ли статус аккаунта на этом этапе. Например:
- Если долг превышает 0,1 TON, аккаунт переходит в статус frozen.
- Если долг превышает 1 TON, аккаунт удаляется.
Фаза вычислений (compute phase)
Эта фаза — одна из самых сложных в транзакции. Здесь выполняется код смарт-контракта, сохранённый в состоянии аккаунта.
В отличие от предыдущих фаз, определение TL-B для этой включает различные варианты.
tr_phase_compute_skipped$0 reason:ComputeSkipReason
= TrComputePhase;
tr_phase_compute_vm$1 success:Bool msg_state_used:Bool
account_activated:Bool gas_fees:Grams
^[ gas_used:(VarUInteger 7)
gas_limit:(VarUInteger 7) gas_credit:(Maybe (VarUInteger 3))
mode:int8 exit_code:int32 exit_arg:(Maybe int32)
vm_steps:uint32
vm_init_state_hash:bits256 vm_final_state_hash:bits256 ]
= TrComputePhase;
cskip_no_state$00 = ComputeSkipReason;
cskip_bad_state$01 = ComputeSkipReason;
cskip_no_gas$10 = ComputeSkipReason;
cskip_suspended$110 = ComputeSkipReason;
Для начала обратите внимание, что фаза вычислений может быть полностью пропущена. В этом случае причина для пропуска указывается явным образом, и может быть одной из следующих:
Причина пропуска | Описание |
---|---|
cskip_no_state | У смарт-контракта нет состояния, а следовательно, и кода, поэтому его исполнение невозможно. |
cskip_bad_state | Возникает в двух случаях: когда поле fixed_prefix_length имеет недопустимое значение или когда StateInit , предоставленный во входящем сообщении, |