Whitepaper · 08
Фреймворк безопасности
Модель угроз, инварианты, управление доступом, граница доверия хуков, управление ключами принудительного исполнения, безопасность обновлений и состояние аудита эталонной реализации A-REX Core.
Профиль безопасности в одном абзаце
У эталонного токена одна точка контроля (_update), фиксированный порядок вычисления, блокировка реентерабельности вокруг всей последовательности хуков, ролевые полномочия, никогда не вытекающие из балансов, хранилище ERC-7201 и небольшая поверхность: восемь внешних функций, изменяющих состояние, помимо ERC-1155. Наиболее мощные возможности — заморозка, принудительный перевод, администрирование хуков, обновления — вынесены в отдельные роли, предназначенные для мультиподписей и таймлоков, и каждое их использование является событием.
Инварианты
| Инвариант | Обеспечивается | Тестируется |
|---|---|---|
totalSupply(id) равно сумме балансов для id | OpenZeppelin ERC1155Supply; transform сжигает, затем эмитирует через _update | тесты родословной (разделение, объединение, переработка) |
status == 255 терминален | setBatchStatus откатывается с ARexInvalidStatusTransition | тесты статусов |
| Погашенную партию нельзя эмитировать или переводить, можно только сжигать | _update | тест погашенной партии |
amount ≤ balance − frozen для перемещений по инициативе держателя | _update; forcedTransfer снижает frozen до balance − amount | тесты заморозки и принудительных переводов |
| Ни один хук не может повторно войти в функцию, изменяющую баланс | блокировка в хранилище устанавливается до проверок и снимается после уведомлений | тест ReentrantHook |
Каждое изменение баланса оценивается как отдельная операция со своими data | _update считывает и очищает контекст операции до любой проверки или внешнего вызова | OpContext.test.js |
| Хуки вычисляются в порядке регистрации; удаление сохраняет порядок | сдвиг массива в removeHook | тест порядка |
| Отклонённая операция не меняет ни одного баланса и не генерирует перевода | проверки выполняются до super._update | тест отказа (число выполнений не меняется) |
| Полномочия никогда не вытекают из баланса | каждая функция, меняющая состояние, защищена ролью или одобрением ERC-1155 | тесты «владение токенами ничего не даёт» |
canTransfer == (transferCheck == 0) | canTransfer определён именно так | тесты конформности |
Модель угроз
Участники и возможности
| Участник | Может | Не может |
|---|---|---|
| Держатель | переводить незамороженные единицы допущенным получателям; сжигать; переформировывать партии того же товара и единицы измерения с сохранением количества на собственный аккаунт (to == from) | менять статус, эмитировать, перерабатывать в другой товар, переформировывать партии на другой аккаунт, перемещать замороженные единицы |
| Одобренный оператор | то же, что держатель, для одобрившего аккаунта | всё, чего не может держатель |
ISSUER_ROLE | создавать партии | перемещать чужие токены |
TRANSFORMER_ROLE | перерабатывающие преобразования и преобразования с выходами на другой аккаунт для токенов, находящихся у держателя роли или одобренных для него | преобразовывать токены без одобрения |
STATUS_ROLE | менять статус (включая погашение), обновлять метаданные | перемещать токены |
ENFORCER_ROLE | замораживать любое количество; принудительно переводить любые токены | обходить хуки (они выполняются с OP_FORCED_TRANSFER) |
HOOK_ADMIN_ROLE | добавлять и удалять хуки | напрямую менять балансы |
PAUSER_ROLE | останавливать всё, кроме принудительных переводов | — |
UPGRADER_ROLE | заменять реализацию | — (это абсолютная власть; см. управление ключами) |
ATTEST_ADMIN_ROLE | решать, какие эмитенты пользуются доверием по каждой теме аттестаций; в эталонных релизах до 2.0.0-alpha.4 также отзывать любую аттестацию (ADR-019, Предложено: административного отзыва НЕ СЛЕДУЕТ допускать в 2.x, и его НЕ ДОЛЖНО быть начиная с 3.0) | аттестовать от имени эмитента |
| Доверенный эмитент | аттестовать партии по своим темам; отзывать собственные аттестации или делегировать право отзыва | аттестовать по теме, по которой ему не доверяют |
| Контракт хука | отклонять операции; наблюдать за ними; отвечать о допуске | повторно входить; менять балансы |
Сценарии
Вредоносный или ошибочный хук (отказ в обслуживании). Хук, всегда отклоняющий операции, блокирует все переводы токена; хук, откатывающийся в check, делает то же самое. Снижение риска: HOOK_ADMIN_ROLE может в любой момент удалить любой хук (порядок сохраняется); PAUSER_ROLE может приостановить работу на время расследования; принудительные переводы по-прежнему выполняются, поэтому принудительное исполнение не теряется. Регистрация проверяет поддержку ERC-165 и ненулевые флаги и отклоняет EOA. Рекомендация: проводить аудит хуков до регистрации; сохранять их число небольшим; размещать администратора за таймлоком, чтобы изменения хуков были видны до вступления в силу.
Повторный вход через хук. onExecuted хука обращается обратно к safeTransferFrom. Снижение риска: блокировка; вызов откатывается с ARexReentrantCall. Следует учесть, что обратный вызов получателя ERC-1155 (onERC1155Received) выполняется после снятия блокировки, как в любом токене ERC-1155; к этому моменту состояние токена окончательно.
Контекст операции в обратных вызовах получателя. Контракт-получатель запускает перевод или сжигание изнутри onERC1155Received. Этот вложенный вызов — OP_TRANSFER или OP_BURN со своими data, а не часть операции, доставившей токены. До 2.0.0-alpha.2 включительно эталонный токен хранил внешнюю операцию в хранилище, пока внешний вызов не завершится, поэтому вложенный вызов после forcedTransfer выполнялся как OP_FORCED_TRANSFER (в обход приостановки, допуска и замороженных балансов), а после transform — как OP_TRANSFORM, который хуки, проверяющие только OP_TRANSFER, пропускали. Снижение риска: _update считывает и очищает контекст операции до любой проверки или внешнего вызова, поэтому вызов из обратного вызова оценивается как самостоятельная операция; OpContext.test.js использует получателя, который переводит и сжигает токены изнутри своего обратного вызова.
Передача через переформирование партий. Оба звена transform запускают хуки с OP_TRANSFORM. Если бы держатель мог переформировать партии на другой аккаунт, товар переходил бы из рук в руки без проверок хуков, которые контролируют только OP_TRANSFER, например обязательной аттестации перед продажей или единиц, заблокированных для доставки. Снижение риска: без TRANSFORMER_ROLE переформирование разрешено только при to == from; переформированный товар попадает к другому держателю обычным переводом, который проходит все правила перевода (ADR-007, поправка от 2026-10-20).
Обход блокировки сжиганием или переформированием. Состояние хука, привязанное к id партии, не переходит вместе с лотом на его выходы. Хук, который блокирует единицы и проверяет только OP_TRANSFER, не мешает держателю сжечь заблокированные единицы или переформировать их на месте в новый id, на который блокировка не распространяется. Снижение риска: DeliveryHook отклоняет с IN_TRANSIT любое сжигание или входное звено transform, которое опустило бы баланс ниже заблокированных единиц; принудительные переводы остаются без ограничений, а после изъятия хук возвращает код, а не откатывается. Хукам, резервирующим единицы, следует проверять OP_BURN и OP_TRANSFORM, а не только OP_TRANSFER.
Компрометация ключа принудительного исполнения. Тот, кто держит ENFORCER_ROLE, может переместить любые токены. Снижение риска: мультиподпись с документированной процедурой (судебное постановление, договор залога, уведомление о санкциях); каждое действие генерирует ForcedTransfer/Frozen; хуки могут требовать ссылку на распоряжение в данных перевода (OP_FORCED_TRANSFER не освобождён от хуков). Для несрочного принудительного исполнения стоит рассмотреть таймлок.
Компрометация ключа обновления. Полная компрометация. Снижение риска: мультиподпись плюс таймлок; проверки обновлений OpenZeppelin в CI; хранилище ERC-7201, исключающее случайные ошибки раскладки.
Злоупотребление статусом. STATUS_ROLE может погасить партию, принадлежащую другим. Это сделано намеренно — склад, инспектор или регулятор объявляет лот непригодным — и должно раскрываться держателям. Снижение риска: назначать подотчётному учреждению; каждое изменение — это событие с указанием причины.
Мошенничество эмитента. ISSUER_ROLE может создать партию, которой физически не существует. Токен не может этому помешать; он делает это прослеживаемым до конкретной стороны (issuer, originRef, metadataHash) и позволяет требовать до перевода аттестаций независимых сторон (склад, инспектор, реестр) через AttestationHook.
Местоположение, скрытое в ссылке на происхождение. Хеш, геохеш или любое другое кодирование координат участка в originRef выглядит анонимным, но его можно вычислить заново, подобрав участок, и его никогда нельзя удалить из реестра. Снижение риска: originRef — это ссылка на доказательство или случайная ссылка; геолокация остаётся офчейн (для EUDR — в Информационной системе ЕС). Проект, предложенный вместе с ADR-019 (Предложено), делает правило «никогда не выводится из местоположения» требованием уровня MUST; это уже было решением ADR-008.
Непроверенные ссылки на исходные партии. Партия, изготовленная из партии другого токена, указывает её в списке upstream своих метаданных. Это утверждение эмитента последующего звена: он может назвать партию, которой никогда не владел, или не указать ту, которой владел. Снижение риска: индексаторы, аудиторы и инструменты отзыва сверяют каждую ссылку с исходным токеном (партия существует, хеш метаданных совпадает, перевод эмитенту действительно состоялся, если указан хеш транзакции) и показывают ссылки, которые проверить не удалось, как непроверенные.
Подмена метаданных. setBatchMetadata изменяет хеш и URI. Снижение риска: доступ ограничен ролью; события BatchMetadataUpdated и URI сохраняют историю; более ранние документы остаются проверяемыми по более ранним хешам.
Front-running transferCheck. Предварительная проверка может устареть до исполнения транзакции (состояние хука изменяется). Транзакция вычисляет всё заново; худший случай — откат с точным кодом.
Gas griefing через хуки. Каждый хук добавляет по одному внешнему вызову на каждый затронутый id на каждой фазе. Хук с неограниченными циклами делает переводы непосильными по стоимости. Снижение риска: правила разработки хуков (ограниченный check), проверка перед регистрацией.
Доверие к обновляемым хукам. Хук, развёрнутый за прокси, может изменить политику переводов без каких-либо изменений в токене. Снижение риска: обращаться с административными ключами хука с такой же заботой, как с HOOK_ADMIN_ROLE; для промышленной эксплуатации предпочитать неизменяемые хуки.
Модель управления доступом
OpenZeppelin AccessControl с восемью ролями у токена и одной — у реестра аттестаций; DEFAULT_ADMIN_ROLE администрирует их все. Рекомендуемая схема для промышленной эксплуатации:
| Роль | Держатель |
|---|---|
DEFAULT_ADMIN_ROLE, UPGRADER_ROLE, HOOK_ADMIN_ROLE | мультиподпись управления за таймлоком |
ENFORCER_ROLE | мультиподпись принудительного исполнения (комплаенс + юристы) |
PAUSER_ROLE | ключ реагирования на инциденты (быстрый) |
ISSUER_ROLE, STATUS_ROLE, TRANSFORMER_ROLE | операционные сервисы с минимальными привилегиями, с ротацией ключей |
ATTEST_ADMIN_ROLE | управление токеном: оно решает, каким эмитентам доверять по каждой теме, но не то, что они аттестуют |
initialize из соображений удобства выдаёт все роли одному администратору; контрольный список развёртывания требует перераспределения перед промышленной эксплуатацией.
Семантика принудительного исполнения и приостановки
- Приостановка блокирует эмиссию, переводы, сжигание и преобразования; она не блокирует принудительные переводы, поэтому законное распоряжение может быть исполнено во время инцидента.
- Принудительные переводы пропускают
canSend/canReceiveи проверку замороженного баланса, уменьшают замороженное количество до остатка и всё равно запускают хуки сOP_FORCED_TRANSFER. Эталонные хуки возвращаютOKдля этой операции; хук, регулирующий принудительное исполнение, может требовать ссылки на распоряжения вdata. - Заморозка может превышать баланс (ERC-7943), поэтому обременение может охватывать будущие зачисления.
Возможности в стандарте, политика в профилях
ADR-019 (Предложено) проводит одну границу через все функции принудительного исполнения. Стандарт фиксирует, что существует и что гарантируется: роли, события, что именно останавливает приостановка, коды AREX:PAUSED и AREX:SANCTIONED, кто может отозвать аттестацию, и правила конфиденциальности. Профиль политики, который ведётся для каждой юрисдикции и каждой организации и версионируется с датами, как нормативный акт, определяет, кто может использовать каждую возможность и на каких условиях:
| Возможность | Фиксируется стандартом | Задаётся профилем политики |
|---|---|---|
| Заморозка и принудительный перевод | setFrozenTokens, forcedTransfer, их события, отдельная роль, отделённая от администрирования, хуки выполняются с OP_FORCED_TRANSFER | можно ли вообще включать роль, кто её держит и со сколькими подписантами, какую правовую ссылку должно содержать каждое использование, кого уведомлять |
| Приостановка | что останавливает приостановка, AREX:PAUSED, принудительные переводы по-прежнему разрешены | кто может приостанавливать и возобновлять работу, какие подтверждение и обоснование требуются, кому сообщается |
| Санкции | AREX:SANCTIONED и смысл canSend / canReceive | какие официальные списки применяются (ООН, OFAC, ЕС, OFSI или их сочетание), как они поддерживаются в актуальном состоянии и то, что совпадение подтверждает человек, прежде чем аккаунт будет помечен |
| Отзыв аттестации | отзывает эмитент, может отзывать отзывающая сторона, которой он делегировал это право; административной роли не следует делать это в 2.x, и она не должна делать это начиная с 3.0 | каким эмитентам токен доверяет по каждой теме; как оператор фиксирует спор |
| Конфиденциальность происхождения | originRef не содержит координат или идентификаторов физических лиц и не выводится из местоположения | какую ссылку использует эмитент и где офчейн хранится геолокация |
Тогда изменение закона становится обновлением профиля, а не новым контрактом или новой версией стандарта.
Безопасность обновлений
- UUPS;
_authorizeUpgradeзащищёнUPGRADER_ROLE. - Структура хранилища ERC-7201 с пространством имён; только добавление полей.
- Проверки плагина OpenZeppelin upgrades выполняются в тестах и должны выполняться в CI перед любой транзакцией обновления.
- Параметр
optimizer.runs = 1на уровне файла удерживает реализацию в пределах EIP-170; изменение, выводящее её за этот предел, приводит к предупреждению при компиляции и к откату при развёртывании.
Безопасность зависимостей и сборки
- OpenZeppelin Contracts и Contracts-Upgradeable зафиксированы на версии 5.3.0; lock-файл закоммичен; CI использует
npm ci. - Solidity 0.8.22, viaIR, EVM
paris(без зависимости от transient storage, можно развёртывать в сетях, не принявших Cancun). - Внешние библиотеки не подключаются; delegatecall вне прокси UUPS не используется.
Состояние аудита
- 46 тестов эталонной реализации, хуков и расширений покрывают каждый из перечисленных выше инвариантов, включая рандомизированный прогон инвариантов с зафиксированным seed.
- Газ и размер измеряются скриптами из репозитория.
- Аудит
ARexToken,ARexAttestationsи эталонных хуков третьей стороной заявлен как предварительное условие для якорного участника в промышленной эксплуатации и для подачи ERC (см. Дальнейшее развитие). - Формальная верификация инвариантов конвейера включена в дорожную карту; инварианты достаточно невелики, чтобы формулироваться как свойства.
Бюллетени безопасности
| Бюллетень | Затрагивает | Исправлено в | Проблемы |
|---|---|---|---|
| AREX-2026-001 | эталонная реализация до 2.0.0-alpha.2 включительно | 2.0.0-alpha.3 | контекст операции просачивался в обратные вызовы получателя ERC-1155; переформирование партий без роли позволяло передать товар другому держателю в обход хуков OP_TRANSFER; блокировки DeliveryHook можно было обойти сжиганием или переформированием |
2.0.0-alpha.3 заменяет 2.0.0-alpha.2. Развёрнутые прокси токена обновляются до новой реализации через UPGRADER_ROLE; схема хранилища не меняется. DeliveryHook не обновляемый: разверните новую версию, зарегистрируйте её через registerHook, удалите старую через removeHook и заново запланируйте активные доставки, так как их блокировки хранятся в хранилище старого хука. Интеграции, которые разделяют или объединяют партии сразу на аккаунт покупателя, должны переформировывать на месте и затем переводить или использовать аккаунт с TRANSFORMER_ROLE. Интерфейсы стандарта не меняются.
Остаточные риски, которые стандарт не устраняет
- Физическое существование и качество лота аттестуются, но не доказываются; токен делает утверждения приписываемыми конкретной стороне, но не истинными.
- Доступность офчейн-метаданных зависит от выбранного хранилища; хеш гарантирует целостность, но не доступность.
- Управление хуками и модулями надёжно лишь настолько, насколько надёжны контролирующие его ключи.