Пост
Поделитесь своими знаниями.

Кодирование BCS в Sui: что это такое и почему это важно
Если вы работаете на Sui или возитесь с Move, вы, вероятно, слышали термин BCS. Это сокращение от слова «машина для форматирования бинарной канонической сериализации», изначально созданная для блокчейна Diem, а теперь ставшая краеугольным камнем экосистем на основе Move, таких как Sui, Aptos, Starcoin и 0L.
Так что да, вам лучше освоиться, если вы серьезно относитесь к строительству в этом пространстве.
Что такое BCS?
Бинарная каноническая сериализация (BCS) — это формат, используемый для сериализации (кодирования) и десериализации (декодирования) структурированных данных в байты.
Вы увидите, что он используется в следующих случаях:
- Кодирование транзакций перед подписанием.
- Отправка или анализ событий из блокчейна.
- Взаимодействие со смарт-контрактами Move вне сети с помощью JavaScript.
Но BCS не включает информацию о типе в байты. Это означает, что при декодировании необходимо заранее знать структуру, в отличие от таких форматов, как JSON или Protocol Buffers, которые описывают себя более легко.
Ключевые особенности BCS
Метаданные типа отсутствуют
Сериализованные выходные данные не содержат подсказок о типах полей. Вы должны знать, с чем имеете дело при декодировании.
Сериализация, зависящая от заказа
Структуры кодируются в точном порядке полей. Измените порядок, и десериализация прервется. Вот почему функции peel_* в Move должны соответствовать макету структуры 1:1.
Универсальный тип
В такой структуре, как:
struct BCSObject<T> has drop, copy {
id: ID,
owner: address,
meta: Metadata,
generic: T
}
Вы можете надежно десериализовать только до метаполя. Универсальные типы затрудняют синтаксический анализ BCS, поэтому всегда ставьте их в последнюю очередь, если хотите безопасно декодировать данные.
Использование BCS в JavaScript
Благодаря библиотеке @mysten /bcs вы можете работать с BCS на JS как профессионал.
npm i @mysten/bcs
и базовый пример:
import { BCS, getSuiMoveConfig } from "@mysten/bcs";
const bcs = new BCS(getSuiMoveConfig());
const ser = bcs.ser(BCS.U16, 10);
console.log(ser.toBytes()); // [0x0a, 0x00]
const des = bcs.de(BCS.U16, ser.toBytes());
console.log(des); // 10
Можно также сериализовать векторы и строки:
bcs.ser("vector<u8>", [1, 2, 3, 4]); // 04 01 02 03 04
bcs.ser(BCS.STRING, "test string"); // 0b7465737420737472696e67
Регистрация пользовательских типов
Допустим, у вас есть следующие структуры Move:
struct Metadata has drop, copy {
name: std::ascii::String
}
struct BCSObject has drop, copy {
id: ID,
owner: address,
meta: Metadata
}
Вы можете зарегистрировать их следующим образом в JS:
bcs.registerStructType("Metadata", {
name: BCS.STRING,
});
bcs.registerStructType("BCSObject", {
id: BCS.ADDRESS,
owner: BCS.ADDRESS,
meta: "Metadata",
});
Пример сериализации и десериализации
Сериализация JavaScript:
const bytes = bcs
.ser("BCSObject", {
id: "0x0000000000000000000000000000000000000005",
owner: "0x000000000000000000000000000000000000000a",
meta: { name: "aaa" }
})
.toString("hex");
console.log("Hex:", bytes);
Вывод может быть:
0x0000000000000000000000000000000000000005000000000000000000000000000000000000000a03616161
Теперь это можно перенести в контракт Move или даже протестировать вручную в интерфейсе командной строки Sui.
Система BCS может показаться низкоуровневой и требует большого количества байтов, но как только вы поймете, как она кодирует данные, вы сможете глубже понять, как работают смарт-контракты Move и как безопасно объединять оффчейн-системы onchain ↔.
А если вы отлаживаете байты BCS в Sui Explorer (как показано ниже):
Кодировка BCS Бинарная каноническая сериализация, или BCS, — это формат сериализации, разработанный в контексте блокчейна Diem и в настоящее время широко используемый в большинстве блокчейнов, основанных на Move (Sui, Starcoin, Aptos, 0L). BCS используется не только в виртуальной машине Move, но и для кодирования транзакций и событий, например для сериализации транзакций перед подписанием или анализа данных событий.
Знание принципов работы BCS крайне важно, если вы хотите глубже понять, как работает Move, и стать экспертом по Move. Давайте углубимся в процесс.
Спецификация и свойства BCS В оставшейся части урока следует помнить о некоторых высокоуровневых свойствах кодирования BCS:
BCS — это формат сериализации данных, в котором полученные выходные байты не содержат никакой информации о типе; поэтому стороне, получающей закодированные байты, необходимо знать, как десериализовать данные
В BCS нет структур (поскольку нет типов); структура просто определяет порядок сериализации полей
Типы оболочек игнорируются, поэтому OuterType и unnestedType будут иметь одинаковое представление BCS:
структура OuterType { владелец: innerType } структура innerType { адрес: адрес } структура unnestedType { адрес: адрес } Типы, содержащие поля универсального типа, можно анализировать вплоть до первого поля универсального типа. Поэтому рекомендуется помещать поля универсального типа в последнюю очередь, если это пользовательский тип, который будет задан или расшифрован.
Примитивные типы, такие как целые числа без знака, кодируются в формате Little Endian
Вектор сериализуется в виде длины ULEB128 (с максимальной длиной до u32), за которой следует содержимое вектора.
Полную спецификацию BCS можно найти в репозитории BCS.
Использование библиотеки JavaScript @mysten /bcs Установка Для этой части вам нужно будет установить библиотеку @mysten /bcs. Вы можете установить ее, введя в корневой каталог нодового проекта:
npm i @mysten /bcs Базовый пример Давайте сначала воспользуемся библиотекой JavaScript для сериализации и десериализации некоторых простых типов данных:
импортируйте {BCS, GetSuimoveConfig} из "@mysten /bcs «;
//инициализируем сериализатор с использованием стандартных конфигураций Sui Move константные блоки = новая BCS (getSuimoveConfig ());
//Определение некоторых типов тестовых данных целое число констант = 10; массив констант = [1, 2, 3, 4]; константная строка = «тестовая строка»
//используйте bcs.ser () для сериализации данных
константа ser_integer = bcs.ser (BCS.U16, целое число);
//используйте bcs.de () для десериализации данных
константа de_integer = bcs.de (BCS.U16, ser_integer.toBytes ());
Давайте подробнее рассмотрим сериализованные и десериализованные поля:
ints — это шестнадцатеричные числа с прямым порядком байтов
0a00 10
первый элемент вектора указывает на общую длину,
тогда это просто те элементы, которые есть в векторе
0401020304 1,2,3,4 Строки # — это просто векторы чисел u8, первый элемент которых равен длине строки 0b7465737420737472696e67 тестовая строка Регистрация типа Мы можем зарегистрировать настраиваемые типы, с которыми будем работать, используя следующий синтаксис:
импортируйте {BCS, GetSuimoveConfig} из "@mysten /bcs «; константная база данных = новая база данных (getSumiMoveConfig ());
//Зарегистрируйте тип метаданных BCS.registerStructType («Метаданные», { имя: BCS.STRING, });
//То же самое для основного объекта, который мы собираемся прочитать Тип структуры BCS.register («Объект BCS», { //BCS.ADDRESS используется как для типов идентификаторов, так и для типов адресов идентификатор: BCS.ADDRESS, владелец: BCS.ADDRESS, метаданные: «Метаданные», }); Использование bcs в смарт-контрактах Sui Давайте продолжим приведенный выше пример со структурами.
Определение структуры Начнем с соответствующих определений структур в контракте Sui Move.
{ //.. структура Metadata удалена, скопируйте { имя: std: :ascii: :Строка }
структура BCSObject удалена, скопируйте { идентификатор: ID, владелец: адрес, метаданные: метаданные } //.. } Десериализация Теперь давайте напишем функцию десериализации объекта в контракте Sui.
//Инициализирует экземпляр bcs bytes пусть bcs = bcs: :new (bcs_bytes);
//Используйте peel_*
функции для удаления значений из сериализованных байтов.
//Порядок должен быть таким же, каким мы пользовались при сериализации!
let (id, владелец, мета) = (
bcs: :peel_address (&mut bcs), bcs: :peel_address (&mut bcs), bcs: :peel_vec_u8 (&mut bcs)
);
//Упаковываем структуру BCSObject с результатами сериализации
BCSObject {id: объект: :id_from_address (id), владелец, метаданные: Metadata {имя: std: :ascii: :string (meta)}}}
Различные методы peel_* в модуле Sui Frame bcs используются для «очистки» каждого отдельного поля от сериализованных байтов BCS. Обратите внимание, что порядок удаления полей должен точно совпадать с порядком полей в определении структуры.
Тест: Почему результаты первых двух вызовов peel_address для одного и того же объекта bcs разные?
Также обратите внимание, как мы преобразуем типы из адреса в идентификатор и из вектора в <8>std: :ascii: :string с помощью вспомогательных функций.
Тест: Что произойдет, если BSCObject будет использовать тип UID вместо типа идентификатора?
Полный пример пользователя и разработчика Полные примеры кодов JavaScript и Sui Move можно найти в папке example_projects.
Сначала мы сериализуем тестовый объект с помощью программы JavaScript:
//Мы создаем тестовый объект для сериализации, обратите внимание, что мы можем указать формат вывода в шестнадцатеричном формате пусть _bytes = bcs .ser («объект BCS», { идентификатор: «0x00000000000000000000000000000000000000000005", владелец: «0x00000000000000000000000000000000000000000000000a», метаданные: {имя: «aaa"} }) .toString («шестнадцатеричное число»); Мы хотим, чтобы на этот раз выходные данные редактора BCS были в шестнадцатеричном формате, который можно указать, как указано выше.
Прикрепите шестнадцатеричную строку результата сериализации к префиксу 0x и экспортируйте в переменную окружения:
экспорт объекта object_hexstring=0x0000000000000000000000000000000000000000000000000000000000000000000000000000000a03616161 Теперь мы можем запустить соответствующие модульные тесты Move, чтобы проверить правильность:
sui move test Вы должны увидеть следующее в консоли:
СБОРКА bcs_move Запуск модульных тестов Move [PASS] 0x0: :bcs_object: :test_десериализация Результат теста: ОК. Всего тестов: 1; пройдено: 1; не удалось: 0 Или мы можем опубликовать модуль (экспортировать PACKAGE_ID) и вызвать метод emit_object, используя приведенную выше сериализованную шестнадцатеричную строку BCS:
клиент sui вызывает функцию emit_object --module bcs_object --package $PACKAGE_ID --args $OBJECT_HEXSTRING Затем мы можем проверить вкладку «События» транзакции в Sui Explorer и убедиться, что мы создали правильно десериализованный объект BCSObject:
- Sui
- SDKs and Developer Tools
Что такое little-endian и big-endian?
Sui is a Layer 1 protocol blockchain designed as the first internet-scale programmable blockchain platform.
Заработай свою долю из 1000 Sui
Зарабатывай очки репутации и получай награды за помощь в развитии сообщества Sui.

- 0xduckmove... SUI+88
1
- harry phan... SUI+61
2
- MiniBob... SUI+57
3
- ... SUIHaGiang+56
- ... SUIRogue+47
- ... SUIRogueRig+44
- ... SUIPeera Admin+25
- ... SUIVens.sui+20
- ... SUIMarlKey+20
- ... SUIdudley_smith+16