Доступность данных (DA) с использованием Celestia
Введение
Технология роллапа снижает стоимость транзакций и увеличивает пропускную способность Ethereum за счет обработки транзакций вне чейна. Транзакции в роллапе сжимаются и размещаются на L1 в пакетах. Пакеты представляют собой тысячи отдельных транзакций вне чейна в рамках одной транзакции на L1. Это позволяет снизить перегрузку базового уровня и уменьшить комиссию для пользователей.
Однако доверять "суммарным" транзакциям, размещаемым на L1, можно только в том случае, если предлагаемое изменение состояния может быть независимо проверено и подтверждено как результат применения всех отдельных транзакций вне чейна. Если операторы роллапа не обеспечивают доступность данных о транзакциях для такой проверки, то это может привести к отправке в L1 некорректных данных.
Эта проблема называется проблемой доступности данных. На самом деле, если можно проверить, что данные, соответствующие блоку роллапа, доступны непосредственно на L1, то любой желающий может взять данные транзакции и пересчитать правильное состояние роллапа. Проблема доступности данных особенно интересна в случае роллапов, поскольку сам роллап работает как база данных вне чейна, и если операторы роллапа не размещают данные на уровне доступности данных, таком как L1, то проверить состояние роллапа становится невозможно.
Более экономичный и масштабируемый вариант роллапа с использованием Celestia в качестве уровня доступности данных
Наиболее очевидным вариантом DA является использование базового монолитного чейна, такого как Ethereum. Полные ноды в монолитной сети совместно используют и хранят необработанные данные о транзакциях, которые включаются в каждый блок валидатором. Имея эти данные, полная нода может пересчитать состояние чейна и сравнить его с любым корнем состояния, зафиксированным валидатором сети. В результате даже в самом худшем случае, если сеть валидаторов скомпрометирована и выдает недействительный блок, полная нода может отклонить этот блок на социальном слое.
Однако любой монолитный чейн, осуществляющий исполнение, консенсус и обеспечение доступности данных на одном единственном P2P-уровне, зачастую не оптимизирован ни для того, ни для другого и, следовательно, может оказаться затратным, если используется для обеспечения доступности данных.
Для сравнения, Arbitrum One платит Ethereum около 112 тыс. долл. в день за обеспечение DA. Это примерно 0,15 долл. США за транзакцию в среднем, учитывая средний дневной объем транзакций на Arbitrum One.
Существует множество приложений, например, игровые на чейне, для которых стоимость транзакции в 0,15 долл. будет совершенно неприемлемой. В связи с этим создаются новые типы сетей, оптимизированные для обеспечения доступности данных. Одним из таких решений является Celestia.
Celestia - это слой доступности данных (DA), обеспечивающий масштабируемое решение проблемы доступности данных. Две ключевые особенности слоя DA Celestia - выборка доступности данных (DAS) и деревья Меркла (Namespaced Merkle trees, NMTs). Обе функции представляют собой новые решения для масштабирования блокчейна: DAS позволяет легким нодам проверять доступность данных, не загружая весь блок; NMT позволяют слоям исполнения и расчета на Celestia загружать транзакции, которые имеют отношение только к ним.
Интеграция Celestia в AltLayer
В AltLayer мы создаем модульные роллапы. Одним из примеров является использование Celestia в качестве слоя доступности данных для роллапов, созданных на платформе AltLayer rollups-as-a-service, что позволяет снизить затраты на обеспечение доступности данных.
Цикл операций
Мы разработали сервис, который выполняет следующие действия
Инициализация
Сначала сервис получает информацию о генезисе и версии ноды роллапа. Он публикует эту информацию в Celestiab (аналогично шагу 4) и обновляет смарт-контракт с указанием высоты блока, на котором хранятся данные.
Извлечение данных о блоках из L2-роллапа
Задача состоит в том, чтобы извлечь из L2 достаточное количество данных для фиксации на уровне DA, чтобы в случае катастрофы можно было восстановить роллап L2. Для этого служба доступности данных будет постоянно получать блоки и доказательства их достоверности пакетно.
Сжатие данных блоков
Для минимизации стоимости данных данные блоков сжимаются с помощью технологии сжатия zlib. Для достижения большей эффективности сжатия данных размер пакета может быть увеличен. Например, при сжатии пакета из 2 блоков размер данных уменьшается на 48%. Если размер пакета увеличить до 100 блоков, то сокращение увеличится до 67%.
Размещение данных в Celestia
Для отправки данных,
Установите легкую ноду Celestia. Легкие ноды обеспечивают доступность данных, в частности,
Укажите область имен, версию и обязательства
Разместите свои данные в Celestia DA используя
blob.Submit
execute(logger, async (data) => {
const blobArr = [
{
namespace: CELESTIA_NODE_NAMESPACE,
data: "0x" + data.toString("hex"),
share_version: CELESTIA_NODE_SHARE_VERSION,
commitment: CELESTIA_NODE_COMMITMENT,
},
];
const height = await jsonRPC(
logger,
CELESTIA_NODE_URL,
"blob.Submit",
[blobArr],
CELESTIA_NODE_AUTH_TOKEN
);
return height;
}).catch(logger.error);
Пример запроса:
curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer <Authorization token>" -d '{
"id": 1,
"jsonrpc": "2.0",
"method": "blob.Submit",
"params": [
[
{
"namespace": "AAAAAAAAAAAAAAAAAAAAAAAAAEJpDCBNOWAP3dM=",
"data": "8fIMqAB+kQo7+LLmHaDya8oH73hxem6lQWX1",
"share_version": 0,
"commitment": "R4DRTAENQ7fGawPbt8aMse1+YReCZYg87xZIZf2fAxc="
}
]
]
}' http://localhost:26658
Celestia API вернет высоту блока, в котором размещены данные
Отслеживание высоты блока, на котором хранятся данные
Чтобы отследить, какой номер блока L2 соответствует высоте блока, на котором размещены данные, смарт-контракт будет развернут на L1. Смарт-контракт будет поддерживать отображение номера блока на высоту блока Celestia.
Для этого сервис вызывает функцию set()
на смарт-контракте и заполняет его соответствующими данными.
key
: Номер стартового блока для данного пакетаvalue
: Номер блока, по которому данные сохраняются в celestiametadata_
: Номер конечного блока для данного пакета
function set(
uint256 key,
uint256 value,
bytes calldata metadata_
) external onlyOperator {
emit Set(key, value, metadata_, _msgSender());
// slither-disable-next-line unused-return
_map.set(key, value);
metadata[key] = metadata_;
latestKey = key;
}
Полный код контракта:
// Идентификатор лицензии SPDX: НЕЛИЦЕНЗИРОВАННО
// СМ. ЛИЦЕНЗИЮ НА https://files.altlayer.io/Alt-Research-License-1.md
// Copyright Alt Research Ltd. 2023. All rights reserved.
//
// Вы признаете и соглашаетесь с тем, что компании Alt Research Ltd. ("Alt Research") (или лицензиары
// Alt Research") принадлежат все законные права, титулы и интересы в отношении
// работы, программного обеспечения, приложений, исходного кода, документации и любых других документов
pragma solidity ^0.8.18;
import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import {EnumerableMapUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableMapUpgradeable.sol";
/// @title UintToUintMap
/// @notice @notice Данный контракт позволяет осуществлять отображение между ключами и значениями uint256 с помощью утилиты EnumerableMapUpgradeable из OpenZeppelin.
/// @dev Контракт также включает ролевую систему доступа.
contract UintToUintMap is AccessControlUpgradeable {
using EnumerableMapUpgradeable for EnumerableMapUpgradeable.UintToUintMap;
/// @notice Идентификатор роли для операторов.
bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
/// @dev Внутреннее хранилище для карты.
EnumerableMapUpgradeable.UintToUintMap private _map;
uint256 public latestKey;
uint256 public globalMetadata;
/// @notice Хранилище открытых метаданных, связанных с каждым ключом.
mapping(uint256 => bytes) public metadata;
/// @dev Эта ошибка возникает, когда в функции `set` длина массивов `keys` и `values` не равна.
error LengthMismatch();
/// @dev Эта ошибка возникает, если вызывающая сторона не является оператором.
error NotOperator();
/// @notice Выдается при установке глобальных метаданных.
event SetGlobalMetadata(uint256 value, address sender);
/// @notice Выдается при установке пары key-value.
event Set(
uint256 indexed key,
uint256 value,
bytes metadata,
address sender
);
/// @custom: конструктор oz-upgrades-unsafe-allow
constructor() {
_disableInitializers();
}
/// @dev Предоставляет возможность вызова функции только операторам.
modifier onlyOperator() {
if (!hasRole(OPERATOR_ROLE, _msgSender())) {
revert NotOperator();
}
_;
}
/// @notice Инициализирует контракт, устанавливает роли администратора и оператора по умолчанию.
/// @param initialDefaultAdmin Адрес первого администратора по умолчанию.
/// @param initialOperator Адрес первого оператора.
function initialize(
address initialDefaultAdmin,
address initialOperator
) external initializer {
_grantRole(DEFAULT_ADMIN_ROLE, initialDefaultAdmin);
_grantRole(OPERATOR_ROLE, initialOperator);
}
/// @notice Задает глобальные метаданные.
/// @param значение ID глобальных метаданных.
function setGlobalMetadata(uint256 value) external onlyOperator {
emit SetGlobalMetadata(value, _msgSender());
globalMetadata = value;
}
/// @notice InsertsВставляет пару ключ-значение в карту и выдает событие Set.
/// @param key Ключ для вставки.
/// @param value Значение для вставки.
/// @param metadata_ Метаданные, связанные с ключом.
function set(
uint256 key,
uint256 value,
bytes calldata metadata_
) external onlyOperator {
emit Set(key, value, metadata_, _msgSender());
// slither-disable-next-line unused-return
_map.set(key, value);
metadata[key] = metadata_;
latestKey = key;
}
/// @notice Возвращает пару key-value, хранящуюся по заданному индексу в карте.
/// @param index Индекс, по которому будет получена пара key-value.
/// @return Key и value по заданному индексу..
function at(uint256 index) external view returns (uint256, uint256) {
return _map.at(index);
}
/// @notice Извлекает из карты значение, связанное с заданным ключом.
/// @param key Ключ для получения значения.
/// @return Значение, связанное с ключом.
function get(uint256 key) external view returns (uint256) {
return _map.get(key);
}
/// @notice Возвращает общее количество пар key-value в карте.
/// @return Общее количество пар key-value.
function total() external view returns (uint256) {
return _map.length();
}
}
Восстановление чейна
Запустите ноду с правильной версией Получите отображение номера блока роллапа на высоту блока Celestia из смарт-контракта L1 Загрузите данные блока из Celestia, вызвав blob.GetAll с правильным пространством имен и высотой блока Распакуйте данные блока импортируйте данные блока в ноду Повторяйте до тех пор, пока нода не будет полностью синхронизирована Запустите ноду с необходимой информацией
Время от времени может возникать необходимость в восстановлении чейна. Это может быть вызвано
катастрофическими событиями, в результате которых оператор ноды вышел из строя на неопределенное время
злонамеренной нодой, отклоняющей или возвращающей оспаривателю недостоверные результаты запроса
В этом случае роллап может быть восстановлен
Получите информацию о генезисе и версии ноды, запустив
blob.GetAll
с указанием нужной области имен и высоты блока
curl -X POST -H "Content-Type: application/json" -H "Authorization: <auth key>" -d '{
"id": 1,
"jsonrpc": "2.0",
"method": "blob.GetAll",
"params": [
110684,
[
"AAAAAAAAAAAAAAAAAAAAAAAAAEJpDCBNOWAP3dM="
]
]
}' http://localhost:26658/
Запустите ноду с правильной версией
Получите из смарт-контракта L1 отображение номера блока роллапа на высоту блока Celestia
Загрузите данные блока из Celestia, запустив blob.GetAll с правильным пространством имен и высотой блока
Распакуйте данные блока
Импортируйте данные блока в ноду
Повторяйте до тех пор, пока нода не будет полностью синхронизирована
Запустите ноду с необходимой информацией
Last updated