Вы здесь

Deep Dive – Move: новый язык программирования блокчейна Libra от компании Facebook

Ключевые характеристики языка Move и его отличия от Ethereum с точки зрения разработчика.

Deep Dive – Move: новый язык программирования блокчейна Libra от компании Facebook

Цель

Ниже будет представлен краткий обзор технической презентации из 26 страниц о новом языке программирования Move для блокчейна Libra от Facebook. Как разработчик Ethereum и энтузиаст блокчейн-сообщества, я ставлю целью вкратце описать основные моменты документа для всех, кто интересуется этим новым языком :)
Надеюсь, что вам понравится, – приятного прочтения!

Краткое описание

Move – это исполняемый язык с байт-кодом, используемый для осуществления пользовательских транзакций и смарт-контрактов.

Здесь необходимо принять к сведению две вещи:

  1. Хотя Move - это язык с байт-кодом, который может быть непосредственно исполнен в виртуальной машине Move, Solidity (язык смарт-контрактов Ethereum) – это язык более высокого уровня, который необходимо скомпилировать до байт-кода перед исполнением в EVM (виртуальная машина Ethereum).
  2. Move может использоваться не только для реализации смарт-контрактов, но и для пользовательских транзакций (см. далее в статье), в то время как Solidity – это язык только для смарт-контрактов в Ethereum.

Ключевой особенностью Move является возможность определять пользовательские типы ресурсов с помощью семантики, основанной на линейной логике: ресурс нельзя скопировать или косвенным образом устранить, его только можно перемещать между местами хранения программ.

Этим он похож на Rust. Значения в Rust могут одновременно присваиваться только одному имени. Присвоение значения другому имени приводит к тому, что оно больше не будет доступно под предыдущим именем.

Например, следующий фрагмент кода выведет ошибку: Use of moved value ‘x’. Это потому, что Rust не имеет «сборщика мусора» (одна из форм автоматического управления памятью). Когда переменные выходят из области видимости, память, на которую они ссылаются, также освобождается. Если совсем просто, то одновременно может быть только один «владелец» данных. В этом примере X является первоначальным владельцем, а затем владельцем становится Y.

1_4J338qTsvi2oGTw0nbmfzw.png

Источник: http://squidarth.com/rc/rust/2018/05/31/rust-borrowing-and-ownership.html

2.2 Кодирование цифровых активов в открытой системе

Есть два свойства, которые присущи физическим активам и которые трудно кодировать в цифровые активы:

  • Ограниченность. Необходимо контролировать добавление активов в систему. Дублирование существующих активов должно быть запрещено, а создание новых активов должно быть привилегированной операцией.
  • Контроль доступа. Участник системы должен иметь возможность защищать свои активы с помощью стратегии контроля доступа.

Это две основные характеристики, которые необходимо достичь цифровым активам, но которые считаются естественными для физических активов. Например, редкий металл является естественным ограниченным ресурсом, и только у вас есть доступ (право собственности) к чеку, позволяющему вам тратить его.

Чтобы наглядно продемонстрировать, как мы пришли к этим двум свойствам, давайте начнем со следующих предложений:

Предложение#1: Простая формула, не предусматривающая ограничение активов и контроль доступа

1__Yuh8LPr7j-DlwSBL85zWw.png

Самая простая формула оценки состояния без ограничения активов и контроля доступа.

  • G[K]:=n обозначает «обновление количества, хранящегося в ключе 𝐾, в общем состоянии блокчейна со значением 𝑛».
  • transaction ⟨Alice, 100⟩ значит «установите баланс счета Алисы на 100».

Но у представленной выше формулы имеется несколько серьезных недостатков:

  • Алиса может иметь неограниченное количество монет, отправляя себе transaction ⟨Alice, 100⟩.
  • Монеты, которые Алиса отправляет Бобу, не имеют никакой ценности, так как Боб может отправить себе неограниченное количество монет, используя ту же технику.

Предложение#2: Учет ограниченности активов

1_6dwFjmtlvRNk3dWrx6qe0w.png

Вторая формула, учитывающая ограниченность активов

В этом случае требуется, чтобы количество монет, хранящихся под 𝐾𝑎, как минимум было равно 𝑛 до того, как произойдет перевод.

Однако, хоть это и решает проблему ограниченности, не производится никакой проверки относительно того, кто может отправлять монеты Алисы. По этой формуле любой может сделать это.

Предложение#3: Учет как ограниченности активов, так и контроля доступа

1_MvaEO3J2nnmMXhV_ssQDwA.png

Третья формула, которая учитывает как ограниченность активов, так и контроль доступа

Мы решаем задачу, используя механизм цифровой подписи verify_sig перед проверкой на допустимое количество активов, что означает, что Алиса использует свой закрытый ключ, чтобы подписать транзакцию и доказать, что она является владельцем своей монеты.

2.3. Существующие языки программирования в блокчейнах

Существующие языки блокчейнов сталкиваются со следующими проблемами (все они были решены в Move):

1. Косвенное представление активов. Актив кодируется с помощью целого числа, но целочисленное значение – это не то же самое, что актив. Фактически, нет типа или значения, которое представляет биткоин/эфир/StrawCoin! Это делает написание программ, в которых используются активы, неудобным и подверженным ошибкам. Такие шаблоны, как перевод активов в процедуры или их вывод из процедур или хранение активов в структурах данных, требуют специальной языковой поддержки.

2. Ограничение по числу активов нельзя расширить. Язык представляет только один ограниченный актив. Кроме того, ограниченность активов жестко закодирована непосредственно в языковой семантике. Программист, который хочет создать пользовательский ресурс, должен внимательно переопределить ограничение без поддержки языка.

Это именно те проблемы, которые возникают в смарт-контрактах Ethereum. Пользовательские активы, такие как токены ERC-20, используют целое число для представления стоимости и общего предложения. Всякий раз, когда выпускаются новые токены, код смарт-контракта должен вручную проверить, был ли достигнут предел (общее предложение в этом случае).

Кроме того, серьезные ошибки, такие как дублирование, повторное использование или потеря активов, с большей вероятностью будут иметь место из-за косвенного представления проблемы активов.

3. Контроль доступа не является гибким. Единственная стратегия контроля доступа, применяемая моделью, - это схема подписи на основе открытого ключа. Подобно ограничению активов, схема контроля доступа встроена в языковую семантику. Непонятно, как расширить язык, чтобы позволить программистам определять пользовательские схемы контроля доступа.

Это также верно для Ethereum, где смарт-контракты не имеют поддержки родного языка для криптографии с открытым и закрытым ключом для выполнения контроля доступа. Разработчики должны вручную прописать функцию контроля доступа, например, использование OnlyOwner.

Несмотря на то, что я большой поклонник Ethereum, я согласен с тем, что эти свойства активов должны изначально поддерживаться языком в целях безопасности.

В частности, перевод эфира в смарт-контракт предполагает динамическую отправку, что привело к появлению нового класса ошибок, известных как уязвимости повторного входа.

Динамическая отправка здесь означает, что логика исполнения кода будет определяться во время работы (динамики), а не во время компиляции (статики). Таким образом, в Solidity, когда контракт A вызывает функцию контракта B, контракт B может запустить код, который не был предвиден разработчиком контракта A, что может привести к уязвимости повторного входа (контракт A случайно выполняет функцию контракта B для снятия денег до фактического вычета остатков со счета).

3. Цель разработки Move

3.1. Ресурсы первого класса

На высоком уровне отношения между модулями/ресурсами/процедурами в Move аналогичны отношениям между классами/объектами/методами в объектно-ориентированном программировании.
Модули Move похожи на смарт-контракты на других языках блокчейна. Модуль объявляет типы ресурсов и процедуры, кодирующие правила создания, уничтожения и обновления объявленных ресурсов.

Модули / ресурсы / процедуры – это всего лишь жаргонизмы в Move. У нас будет пример, чтобы проиллюстрировать это позже в этой статье ;)

3.2. Гибкость

Move дает гибкость блокчейну Libra благодаря скриптам транзакций. Каждая транзакция в блокчейне Libra включает скрипт (сценарий) транзакции, который фактически является основной процедурой транзакции.

Скрипты могут выполнять либо выраженные одноразовые действия (например, выплата определенному количеству получателей), либо многоразовые действия (путем вызова одной процедуры, инкапсулирующей логику многоразового действия)

Из вышесказанного мы видим, что скрипт транзакций в Move обеспечивает большую гибкость, поскольку он способен как к одноразовым действиям, так и многоразовым действиям, в то время как Ethereum может выполнять только многоразовые действия (что задействует только метод смарт-контрактов). Причина, по которой он называется «многоразовым», заключается в том, что функции смарт-контракта могут выполняться несколько раз.

3.3. Безопасность

Исполняемый формат Move – это типизированный байт-код, который имеет более высокий уровень, чем узел, но еще более низкий уровень, чем исходный язык. Байт-код проверяется в сети на безопасность ресурсов, типов и памяти верификатором байт-кода, а затем исполняется непосредственно интерпретатором байт-кода. Этот выбор позволяет Move предоставлять гарантии безопасности, обычно связанные с исходным языком, но без добавления исходного компилятора в достоверную вычислительную базу или стоимости компиляции в критический путь (это задача или последовательность задач, определяющая дату окончания проекта) для выполнения транзакции.

Именно до мелочей продуманный дизайн Move позволяет ему быть байт-кодовым языком. Поскольку его не нужно компилировать из источника в байт-код, как, например Solidity, ему не нужно беспокоиться о возможных сбоях или атаках в компиляторах.

3.4. Проверяемость

Наш подход заключается в том, чтобы выполнить максимально легкую проверку ключевых свойств безопасности в сети, но и сделать так, чтобы схема языка Move поддерживала передовые инструменты для статической проверки вне сети.

Отсюда мы видим, что Move предпочитает выполнять статическую проверку, а не выполнять проверку в сети. Тем не менее, как указано в конце их документа, разработка инструмента проверки намечена на будущее.

3. Модульность. Перемещение модулей обеспечивает абстракцию данных и локализацию критических операций с ресурсами. Инкапсуляция, инициируемая модулем в сочетании с защитой, обеспечиваемой системой типов Move, гарантирует, что свойства, установленные для типов модуля, не могут быть нарушены кодом вне модуля.

Это также очень хорошо продуманный дизайн абстракции данных! это означает, что данные в смарт-контракте могут быть изменены только в рамках контракта, но не в других контрактах извне.

1_Ep74D_K5reVUBmL9xAqJ_w.png

Источник: https://libra.org/en-US/open-source-developers/#move_carousel

4. Обзор Move

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

В этом разделе приведен пример того, какие модули, ресурсы и процедуры используются при написании языка программирования.

4.1. Скрипт одноранговой платежной транзакции

1_IfbpCjAiQarURpXph7HiEQ.png

Сумма монет будет переведена от отправителя транзакции получателю платежа

Здесь есть несколько новых символов (мелкий красный текст – это мои собственные заметки XD):

  • 0x0: адрес учетной записи, в которой хранится модуль
  • Currency: название модуля
  • Coin: тип ресурса
  • Значение coin, возвращаемое процедурой, является значением ресурса, тип которого 0x0.Currency.Coin
  • move(): значение не может быть использовано снова
  • copy(): значение можно использовать позже

Разбивка кода:

На первом этапе отправитель вызывает процедуру с именем withdraw_from_sender из модуля, хранящегося в 0x0.Currency.

На втором этапе отправитель переводит средства получателю, перемещая значение ресурса coin в депозитную процедуру модуля 0x0.Currency.

Ниже приводятся 3 типа примеров кода, которые будут отклоняться:

1. Дублирование валюты путем изменения move(coin) на copy(coin)

Значения ресурсов можно только перемещать. Попытка дублировать значение ресурса (например, используя copy (coin) в приведенном выше примере) вызовет ошибку во время проверки байт-кода.

Поскольку coin – это значение ресурса, его можно только перемещать.

2. Повторное использование валюты за счет написания move(coin) дважды

Добавление строки 0x0.Currency.deposit(copy(some_other_payee), move (coin)) к приведенному выше примеру позволит отправителю «потратить» монету дважды: первый раз с участием payee (получателя), а второй – с some_other_payee (другим получателем). Такое нежелательное поведение невозможно с физическим активом. К счастью, Move отвергнет эту программу.

3. Потеря валюты из-за неупоминания move(coin)

Неперемещение ресурса (например, путем удаления строки, содержащей move (coin) в приведенном выше примере) вызовет ошибку проверки байт-кода. Это защищает программистов Move от случайной — или намеренной – потери следа ресурса.

4.2. Модуль Currency

4.2.1 Primer: модель исполнения Move

1_VmEi58-4ApyxoBe5bAAOXQ.png

Каждая учетная запись может содержать ноль или более модулей (изображенных в виде прямоугольников) и один или более значений ресурса (изображенных в виде цилиндров). Например, учетная запись по адресу 0x0 содержит модуль 0x0.Currency и значение ресурса типа 0x0.Currency.Coin. Учетная запись по адресу 0x1 имеет два ресурса и один модуль; учетная запись по адресу 0x2 имеет два модуля и одно значение ресурса.

Несколько важных моментов:

  • Исполнение скрипта транзакции – это все или ничего
  • Модуль – это долгоживущий фрагмент кода, публикуемый в глобальном состоянии
  • Глобальное состояние структурировано как карта от адресов учетных записей до учетных записей
  • Учетные записи могут содержать не более одного значения ресурса заданного типа и не более одного модуля с заданным именем (учетная запись по адресу 0x0 не может содержать дополнительный ресурс 0x0.Currency.Coin или другой модуль с именем Currency)
  • Адрес объявляющего модуля является частью типа (0x0.Currency.Coin и 0x1.Currency.Coin являются различными типами, которые не могут использоваться взаимозаменяемо)
  • Программисты могут по-прежнему хранить несколько экземпляров данного типа ресурсов в учетной записи, определяя пользовательский ресурс-оболочку

(resource TwoCoins { c1: 0x0.Currency.Coin, c2: 0x0.Currency.Coin })

  • По правилу, все хорошо до тех пор, пока вы можете ссылаться на ресурс по его имени без конфликтов, например, вы можете ссылаться на два ресурса, используя TwoCoins.c1 и TwoCoins.c2.

4.2.2 Объявление ресурса Coin

1_wWa6AJEASkqQGPpUF7ovpQ.png

Модуль с именем Currency и тип ресурса с именем Coin, управляемый модулем
Несколько важных моментов:

  • Coin (монета) – это тип структуры с одним значением поля типа u64 (64-разрядное целое число без знака)
  • Только процедуры модуля Currency могут создавать или уничтожать значения типа Coin
  • Другие модули и скрипты транзакций могут только писать или ссылаться на поле значения через открытые модулем процедуры 

4.2.3 Осуществление процедуры Deposit

1_dq0RLbNdoSbv6BZDY0WTuA.png

Эта процедура принимает ресурс Coin в качестве входных данных и объединяет его с ресурсом Coin, хранящимся в учетной записи получателя платежа путем:
  1. Уничтожения входного параметра Coin и записи его значения.
  2. Получения ссылки на уникальный ресурс Coin, хранящийся в учетной записи получателя.
  3. Увеличения значения Coin получателя на значение Coin, переданного в процедуру.

Несколько важных моментов:

  • Unpack, BorrowGlobal – встроенные процедуры
  • Unpack - единственный способ удалить ресурс типа T. Он принимает в качестве входных данных ресурс типа T, уничтожает его и возвращает значения, привязанные к полям ресурса
  • BorrowGlobal принимает адрес в качестве входных данных и возвращает ссылку на уникальный экземпляр T, опубликованный по этому адресу
  • &mut Coin является изменяемой ссылкой на ресурс Coin , а не Coin

4.2.4 Осуществление процедуры withdraw_from_sender

1_a1yLuPSVQaErVBQwCURuCw.png

Эта процедура:
  1. Получает ссылку на уникальный ресурс типа Coin, опубликованный под учетной записью отправителя.
  2. Уменьшает значение указанной Coin на входную сумму.
  3. Создает и возвращает новую Coin с суммой значения.

Несколько важных моментов:

  • Deposit может быть вызван кем угодно, но withdraw_from_sender имеет контроль доступа, который может быть вызван только владельцем монеты
  • GetTxnSenderAddress подобен msg.sender в Solidity
  • RejectUnless похож на require в Solidity. Если эта проверка не проходит, выполнение текущего сценария транзакции останавливается, и ни одна из выполненных операций не будет применена к глобальному состоянию
  • Pack, также встроенная процедура, создает новый ресурс типа T
  • Как и Unpack, Pack может быть вызван только внутри объявляющего модуля ресурса T

Заключение

Теперь у вас имеется представление об основных характеристиках Move, его отличиях от Ethereum, а также о его базовом синтаксисе.

Я также настоятельно рекомендую прочитать оригинал технической презентации. Она включает в себя много подробностей относительно принципов проектирования языка программирования и много полезных ссылок.

Спасибо за прочтение. Делитесь этой информацией со всеми, кто в теме :) Любые замечания приветствуются!!

Меня зовут Тинг Тинг , в настоящее время я являюсь научным сотрудником блокчейн-лаборатории UC Berkeley, соучредителем и техническим директором Turing Chain, консалтинговой блокчейн-компании (среди наших партнеров – J. P. Morgan, BASF, Acer, Binance Labs и компании в APAC и США).

По вопросам сотрудничества пишите на мою почту [email protected] :)

Категория: 
Tutorial
1
Ваша оценка: Нет Средняя: 1 (1 оценка)
15943 / 0
Аватар пользователя Serg Demin
Публикацию добавил: Serg Demin
Дата публикации: пт, 12/13/2019 - 10:04

Что еще почитать: