Sui.

Пост

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

article banner.
0xduckmove.
May 30, 2025
Статья

Кодирование 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 { адрес: адрес } Типы, содержащие поля универсального типа, можно анализировать вплоть до первого поля универсального типа. Поэтому рекомендуется помещать поля универсального типа в последнюю очередь, если это пользовательский тип, который будет задан или расшифрован.

структура BCSObject удалена, скопируйте { идентификатор: ID, владелец: адрес, метаданные: метаданные, общий: T } В этом примере мы можем десериализовать все, вплоть до метаполя.

Примитивные типы, такие как целые числа без знака, кодируются в формате 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, целое число); константа ser_array = bcs.ser («вектор», массив); константа ser_string = bcs.ser (строка BCS.STRING);

//используйте bcs.de () для десериализации данных константа de_integer = bcs.de (BCS.U16, ser_integer.toBytes ()); константа de_array = bcs.de («вектор», ser_array.toBytes ()); константа de_string = bcs.de (BCS.STRING, ser_string.toBytes ()); Мы можем инициализировать экземпляр сериализатора с помощью встроенной настройки по умолчанию для Sui Move, используя приведенный выше синтаксис, новую BCS (GetSuimoveConfig ()).

Существуют встроенные перечисления, которые можно использовать для типов Sui Move, таких как BCS.U16, BCS.STRING и т. д. Для универсальных типов их можно определить, используя тот же синтаксис, что и в Sui Move, например, вектор в приведенном выше примере.

Давайте подробнее рассмотрим сериализованные и десериализованные поля:

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.

общедоступный объект from_bytes (bcs_bytes: вектор): bcsObject {

//Инициализирует экземпляр 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
1
Поделиться
Комментарии
.
harry phan.
May 30 2025, 17:12

Что такое little-endian и big-endian?

Sui is a Layer 1 protocol blockchain designed as the first internet-scale programmable blockchain platform.

306Посты450Ответы
Sui.X.Peera.

Заработай свою долю из 1000 Sui

Зарабатывай очки репутации и получай награды за помощь в развитии сообщества Sui.

Кампания вознагражденийИюнь
Мы используем файлы cookie, чтобы гарантировать вам лучший опыт на нашем сайте.
Подробнее