Комиссии на Solana
Блокчейн Solana имеет несколько различных видов сборов и затрат, которые взимаются за использование сети без права доступа. Их можно разделить на несколько конкретных типов:
- Плата за транзакцию - комиссия для проверки транзакций/инструкций
- Плата за приоритизацию - необязательная комиссия для ускорения транзакций
- Аренда - удерживаемый баланс для хранения данных в цепи
Плата за транзакцию #
Небольшая комиссия, оплаченная для обработки логики (инструкции) в рамках on-chain программы на блокчейне Солана известна как "transaction fee".
По мере того как каждая транзакция (содержащая одну или несколько инструкций) отправляется по сети, она обрабатывается текущим лидером валидаторов. После подтверждения в качестве транзакции глобального состояния эта плата за транзакцию выплачивается сети, чтобы помочь поддержать экономический дизайн блокчейна Solana.
Плата за транзакцию отличается от платы за хранение данных на счете в виде арендной платы. В то время как плата за транзакцию взимается за обработку инструкций в сети Solana, рентный депозит удерживается на счете для хранения его данных в блокчейне и может быть возвращен.
В настоящее время базовая комиссия за транзакции в Solana установлена на статическом уровне в 5 тыс. ламопортов за подпись. В дополнение к этой базовой комиссии могут быть добавлены любые дополнительные сборы за приоритизацию.
Зачем платить за транзакцию? #
Комиссия за транзакции дает много преимуществ в Solana economic design, в основном, они:
- предоставить компенсацию сети валидаторов за потраченные процессоры/GPU вычислить ресурсы, необходимые для обработки транзакций
- снижение сетевого спама, введя реальную стоимость транзакций
- обеспечить долгосрочную экономическую стабильность сети с помощью протокола, фиксированного в минимальной суммы комиссии за транзакцию
Базовый экономический дизайн #
Многие блокчейн сети (включая биткойны и Ethereum) полагаются на инфляцию по protocol-based rewards для обеспечения безопасности сети в краткосрочной перспективе. В долгосрочной перспективе эти сети будут все больше полагаться на on transaction fees для поддерживать безопасность.
То же самое можно сказать и к Solana. В частности:
- Фиксированная доля (первоначально 50%) каждой комиссии за транзакцию равна burned (уничтожить), с оставшимся переходом к текущему [leader](/docs/terminology. d#leader) обработка транзакции.
- A scheduled global inflation rate provides a source for rewards distributed to Solana Validators.
Коллекция комиссий #
Транзакции должны иметь хотя бы одну учетную запись, которая подписала транзакцию и может быть записана. Эти счета подписывающих лиц с возможностью записи сериализуются первыми в списке счетов, и первый из них всегда используется в качестве "fee payer".
Перед обработкой любых инструкций по транзакциям с баланса счета плательщика комиссии будет списана сумма, необходимая для оплаты комиссии за транзакцию. Если баланс плательщика комиссии недостаточен для покрытия комиссии за транзакцию, обработка транзакции будет остановлена и приведет к неудачной транзакции.
Если баланс достаточен, комиссии будут списаны, и начнется выполнение инструкций транзакции. Если какая-либо из инструкций приведет к ошибке, обработка транзакции будет остановлена и в конечном итоге будет записана как неудачная транзакция в Solana ledger. Плата за эти неудачные транзакции по-прежнему взимается временем выполнения.
Если какая-либо из инструкций приведет к ошибке или нарушит ограничения времени выполнения, все изменения в учетной записи, кроме вычета комиссии за транзакцию, будут отменены. Это связано с тем, что сеть валидаторов уже затратила вычислительные ресурсы на сбор транзакций и начало первичной обработки.
Распределение комиссий #
Комиссионные сборы [частично сгорел](https://github.com/anza-xyz/agave/blob/b7bbe36918f23d98e2e73502e3c4cba78d395ba9/runtime/src/bank/fee_distribution. s#L55-L64) и остальные комиссии собираются валидатором, который произвел блок в который были включены соответствующие транзакции. В частности, 50% сжигается, а 50% распределяется между валидаторами, которые произвели блок.
Зачем сжигать комиссии? #
Как упоминалось выше, фиксированная доля каждой комиссии за транзакцию составляет burned (уничтожено). Экономическая стоимость SOL должна быть определена и таким образом поддерживает безопасность сети. В отличие от схемы, в которой плата за транзакции полностью сгорает, лидеры все равно заинтересованы в том, чтобы включить как можно больше транзакций в свои слоты (возможность создать блок).
Сгоревшие комиссии также могут помочь предотвратить цензуру транзакций со стороны злонамеренных валидаторов, поскольку они учитываются при выборе форка.
Пример атаки: #
В случае форка Proof of History (PoH) со злонамеренным или цензурирующим лидером:
- из-за комиссий, потерянных из-за цензуры, мы ожидаем, что общее количество сожженных комиссий будет меньше, чем у сопоставимого честного форка.
- если лидер-цензор должен компенсировать эти потерянные протокольные сборы, ему придется самому заменить сгоревшие сборы на своем форке
- таким образом, потенциально снижая стимул к цензуре в первую очередь
Расчет комиссии за транзакции #
Полная плата за данную транзакцию рассчитывается на основе двух основных частей:
- статически устанавливать базовую плату за подпись, и
- вычислительные ресурсы, используемые во время транзакции, измеряются в "compute units"
Поскольку каждая транзакция может требовать разного количества вычислительных ресурсов, каждой из них выделяется максимальное количество вычислительных единиц на транзакцию в рамках бюджета вычислений.
Расчет бюджета #
Чтобы предотвратить злоупотребление вычислительными ресурсами, каждая транзакция выделяется "calculte budget". Этот бюджет содержит подробную информацию о вычислительных устройствах и включает в себя:
- расчетные расходы, связанные с различными видами операций, которые может выполнить транзакция (вычислительные единицы, потребляемые за операцию),
- максимальное количество вычислительных единиц, которое может потратить транзакция (рассчитать единицы ограничения),
- и операционные границы транзакции должны придерживаться (такие как данные учетной записи размер)
Если транзакция израсходовала весь бюджет вычислений (исчерпание бюджета вычислений) или превысила ограничения, например, превысила максимальную глубину стека вызовов или максимальный размер загруженных данных счета, время выполнения останавливает обработку транзакции и возвращает ошибку. В результате транзакция завершается неудачей, а состояние не изменяется (кроме того, что взимается комиссия за транзакцию).
Лимит трафика клиентов #
В транзакции может быть указано максимальное количество байт данных учетной
записи, которым разрешено загрузить, включив инструкцию
SetLoadedAccountsDataSizeLimit
(не чтобы превышать абсолютного максимума
runtim). Если нет SetLoadedAccountsDataSizeLimit
, то по умолчанию транзакция
будет использовать время выполнения
MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES
значение.
Для создания этой инструкции можно использовать функцию ComputeBudgetInstruction::set_loaded_accounts_data_size_limit:
Вычислительные единицы
Вычислить единицы #
Все операции, выполняемые на цепи в рамках транзакции, требуют от валидаторов разного количества вычислительных ресурсов при обработке (стоимость вычислений). Наименьшая единица измерения потребления этих ресурсов называется "вычислительной единицей"
По мере обработки транзакции вычислительные единицы постепенно расходуются каждой из ее инструкций, выполняемых на цепочке (расходуется бюджет). Поскольку каждая инструкция выполняет различную логику (запись на счета, cpi, выполнение системных вызовов и т. д.), каждая из них может потреблять разное количество вычислительных единиц.
A program can log details about its compute usage, including how much remains in its allotted compute budget. You can also find more information in this guide for optimizing your compute usage.
Each transaction is allotted a compute unit limit, either with the default limit set by the runtime or by explicitly requesting a higher limit. Если транзакция превысила свой лимит вычислительных единиц, ее обработка останавливается, что приводит к сбою транзакции.
Ниже перечислены некоторые распространенные операции, которые требуют затрат на вычисления:
- выполнение инструкций
- передача данных между программами
- выполнение системных вызовов
- использование sysvars
- логирование с помощью макроса
msg!
- ведение журнала Pubkeys
- создание адресов программы (PDA)
- межпрограммные вызовы (CPI)
- криптографические операции
При межпрограммных вызовах вызываемая инструкция наследует бюджет вычислений и ограничения своего родителя. Если вызываемая инструкция израсходует оставшийся бюджет транзакции или превысит ограничения, вся цепочка вызовов и обработка транзакции верхнего уровня будут остановлены.
Более подробную информацию обо всех операциях, потребляющих вычислительные единицы, можно найти в разделе ComputeBudget среды выполнения Solana.
Предел вычислений #
Каждая транзакция имеет максимальное количество вычислительных блоков (CU), которое она может потреблять, называемое "лимитом вычислительных блоков". Для каждой транзакции среда выполнения Solana имеет абсолютный максимальный предел вычислительных единиц в 1,4 миллиона CU и устанавливает по умолчанию запрашиваемый максимальный предел в 200 тысяч CU на инструкцию.
Транзакция может запросить более конкретный и оптимальный лимит вычислений от
включая одну инструкцию SetComputeUnitLimit
. Либо выше либо ниже предела . Но
он никогда не может потребовать больше абсолютного максимального предела на
каждую транзакцию .
Хотя лимит вычислений транзакции по умолчанию будет работать в большинстве случаев для простых транзакций, часто они менее чем оптимальны (как для рабочего времени, так и для пользователя). Для более сложных операций, таких как вызов программ, выполняющих несколько ИВК, вам может потребоваться запросить более высокий лимит вычислительных единиц для транзакции транзакции.
Запрос оптимального лимита вычислительной единицы для транзакции крайне важен для поможет вам оплатить транзакцию меньше и лучше планировать транзакцию в сети. Кошельки, dApps и другие сервисы должны обеспечить оптимальные запросы к вычислительным блокам, чтобы предоставить своим пользователям наилучшие возможности.
Для получения более подробной информации и лучших методов ознакомьтесь с этим руководством по теме запрос оптимальных вычислительных ограничений.
Рассчет цены единицы #
Если транзакция желает заплатить более высокую плату, чтобы повысить приоритет обработки, она может установить "цену вычислительной единицы". Эта цена, в сочетании с лимитом вычислительных единиц, будет использоваться для определения платы за приоритет транзакции.
По умолчанию цена за единицу вычисления не установлена, что не влечет за собой дополнительной платы за приоритизацию.
Плата приоритизации #
В рамках Расчётного бюджета время выполнения поддерживает транзакции с оплатой необязательной комиссии, известной как "комиссия приоритизации". Выплата этой дополнительной комиссии помогает повысить приоритетность транзакции к другим во время обработки, что приводит к более быстрому времени исполнения.
Как рассчитывается комиссия за приоритизацию #
Комиссия за приоритизацию транзакции рассчитывается перемножением её compute единицы limit на compute unit price (измеряется в micro-lamports). Эти значения можно установить один раз для каждой транзакции, включив следующие инструкции Compute Budget:
- SetComputeUnitLimit - установка максимального количества вычислительных единиц, которые может потреблять транзакция
- SetComputeUnitPrice - установка желаемой дополнительной платы, которую транзакция готова заплатить за повышение приоритета.
Если нет инструкции SetComputeUnitLimit
, то будет использоваться
ограничение вычислений по умолчанию.
Если инструкция SetComputeUnitPrice не указана, транзакция по умолчанию будет работать без дополнительной повышенной платы и с самым низким приоритетом (т. е. без платы за приоритизацию).
Как установить комиссию за приоритизацию #
Комиссия за приоритизацию транзакции устанавливается с помощью инструкции
SetComputeUnitPrice
и, опционально, инструкции SetComputeUnitLimit
.
Выполнение будет использовать эти значения для вычисления комиссии за
приоритизацию, которая будет использоваться в приоритезации данной транзакции в
блоке.
Вы можете создать каждую из этих инструкций с помощью их функций Rust или @solana/web3.js. Затем каждая инструкция может быть включена в транзакцию и отправлена на кластер обычным образом. См. также лучшие практики ниже.
В отличие от других инструкций внутри транзакции Solana, инструкции для расчета бюджета НЕТ требуют каких-либо счетов. Транзакция с несколькими либо инструкциями не будет выполнена.
Транзакции могут содержать только по одной инструкции каждого типа вычислительного бюджета. Дублирование типов инструкций приведет к ошибке TransactionError::DuplicateInstruction и, в конечном счете, к отказу транзакции.
Rust #
Контейнер rust solana-sdk включает функции в составе ComputeBudgetInstruction для создания инструкций по установке лимита вычисляемых единиц и цены вычисляемых единиц:
let instruction = ComputeBudgetInstruction::set_compute_unit_limit(300_000);
let instruction = ComputeBudgetInstruction::set_compute_unit_limit(300_000);
Javascript #
The @solana/web3.js
library includes functions within the
ComputeBudgetProgram
class to craft instructions for setting the compute unit limit and compute
unit price:
const instruction = ComputeBudgetProgram.setComputeUnitLimit({
units: 300_000,
});
const instruction = ComputeBudgetProgram.setComputeUnitPrice({
microLamports: 1,
});
Наилучшие практики в оплате приоритизации #
Ниже приведена общая информация о лучших практиках определения приоритета вычислений. В этом руководстве вы также найдете более подробную информацию о том, как запрашивать оптимальные вычисления, в том числе о том, как смоделировать транзакцию, чтобы определить ее приблизительное использование.
Запрашивать минимальное количество вычислительных единиц #
Транзакции должны запрашивать минимальное количество вычислительных единиц, необходимых для выполнения, чтобы минимизировать плату. Также обратите внимание, что плата не корректируется, если количество запрашиваемых вычислительных единиц превышает количество вычислительных единиц, фактически потребляемых выполняемой транзакцией.
Получение последних данных о плате за приоритизацию #
Перед отправкой транзакции в кластер вы можете использовать метод RPC getRecentPrioritizationFees, чтобы получить список последних оплаченных сборов за приоритизацию в последних блоках, обработанных узлом.
Затем вы можете использовать эти данные для оценки соответствующей платы за приоритизацию для вашей транзакции, чтобы (а) гарантировать, что она будет обработана кластером, и (б) минимизировать уплаченные сборы.
Аренда #
Плата, вносимая на каждый аккаунт Solana для поддержания связанных с ним данных в цепи, называется "арендной платой". Эта плата удерживается из обычного баланса lamport на каждом счете и может быть возвращена при закрытии счета.
Арендная плата отличается от платы за транзакции. Арендная плата "платится" (удерживается со счета) за хранение данных на блокчейне Solana и может быть возвращена. В то время как плата за транзакции взимается за обработку инструкций в сети.
Все аккаунты должны поддерживать достаточно высокий баланс lamport (по отношению к выделенному им пространству), чтобы освободиться от арендной платы и оставаться в блокчейне Solana. Любая транзакция, которая попытается уменьшить баланс аккаунта ниже минимального баланса, необходимого для освобождения от арендной платы, завершится неудачей (если только баланс не будет уменьшен ровно до нуля).
Когда владелец счета больше не желает хранить эти данные на цепочке и в глобальном состоянии, он может закрыть счет и вернуть себе рентный депозит.
Это достигается путем снятия (перевода) всего баланса lamport на другой счет (т. е. в ваш кошелек). Уменьшив баланс счета ровно до 0, среда выполнения удалит счет и связанные с ним данные из сети в процессе "сборки мусора".
Стоимость аренды #
Ставка аренды Solana устанавливается в масштабе всей сети, в основном на основе установленного во время выполнения "lamports per byte per year". Currently, the rent rate is a static amount and stored in the Rent sysvar.
Эта арендная ставка используется для расчета точной суммы арендной платы, которую необходимо удерживать с учетной записи за выделенное ей пространство (т. е. объем данных, который можно хранить в учетной записи). Чем больше места отведено под учетную запись, тем выше будет удерживаемая арендная плата.
Освобождение от аренды #
Accounts must maintain a lamport balance greater than the minimum required to store its respective data on-chain. This is called "rent exempt" and that balance is called the "minimum balance for rent exemption".
Новые учетные записи (и программы) на Solana ОБЯЗАТЕЛЬНО должны быть инициализированы с достаточным количеством лампортов, чтобы стать освобожденными от аренды. Так было не всегда. Раньше время выполнения периодически автоматически взимало плату с каждой учетной записи, баланс которой был ниже минимального для освобождения от арендной платы. В конечном итоге баланс этих учетных записей уменьшался до нуля, и они собирались в глобальном состоянии (если не пополнялись вручную).
В процессе создания новой учетной записи вы должны убедиться, что внесли достаточное количество ламопортов, чтобы минимальный баланс был выше этого порога. Все, что ниже этого минимального порога, приведет к неудачной транзакции.
Каждый раз при уменьшении баланса учетной записи выполняется проверка, будет ли он по-прежнему выше минимального баланса для освобождения от арендной платы. Если конечный баланс не уменьшится ровно до 0 (закрытие счета), транзакции, в результате которых баланс учетной записи опустится ниже порога освобождения от арендной платы, будут неудачными.
Конкретный минимальный баланс для освобождения учетной записи от арендной платы зависит от текущей арендной ставки блокчейна и желаемого объема пространства для хранения данных (размер учетной записи). Поэтому рекомендуется использовать RPC-конечную точку getMinimumBalanceForRentExemption для расчета минимального баланса для данного размера учетной записи.
The required rent deposit amount can also be estimated via the
solana rent
CLI subcommand:
solana rent 15000
# output
Rent per byte-year: 0.00000348 SOL
Rent per epoch: 0.000288276 SOL
Rent-exempt minimum: 0.10529088 SOL
Сборка мусора #
Учетные записи, баланс которых не превышает нуля, удаляются из сети в процессе, известном как сборка мусора. Этот процесс выполняется для того, чтобы уменьшить объем хранимых в сети данных, которые больше не используются/не поддерживаются.
После того как транзакция успешно уменьшает баланс учетной записи до 0, сборка мусора происходит автоматически во время выполнения. Любая транзакция, которая попытается уменьшить баланс учетной записи ниже минимального баланса для освобождения от арендной платы (который не равен нулю), завершится неудачей.
Важно отметить, что сборка мусора происходит после завершения выполнения транзакции. Если есть команда "закрыть" учетную запись, уменьшив ее баланс до нуля, она может быть "снова открыта" в той же транзакции с помощью более поздней команды. Если состояние счета не было очищено в инструкции "закрыть", то в последующей инструкции "снова открыть" состояние учетной записи будет таким же. Это забота о безопасности, поэтому полезно знать точное время, когда сборка мусора вступает в силу.
Даже после удаления учетной записи из сети (через сборку мусора) у нее могут оставаться транзакции, связанные с ее адресом (как в прошлом, так и в будущем). Даже если в проводнике блоков Solana может появиться сообщение типа «учетная запись не найдена», вы все равно сможете просмотреть историю транзакций, связанных с этой учетной записью
You can read the validator implemented proposal for garbage collection to learn more.