Coin Lab

1 7394 0
Solidity I. Введение в смарт-системы смарт-контрактов

Engineering DAPPs
Автор: Андреас Олофссон Andreas

Андреас является центральным разработчиком для библиотек смарт-контрактов в компании « Eris Industries». Андреас – специалист по программному обеспечению по разработке открытых исходных текстов и построению приложений для децентрализованных систем, большей частью для Ethereum и приложений, основанных на Eris. Он однозначно является лучшим сотрудником в Eris Industries.

Часть 1: Модель пяти типов

АвторАндреас Олофссон, центральный разработчикОбразование и просвещение

Примечание (1): Это первое пособие в серии, состоящей из шести частей по Solidity.

Примечание (2): Мы также опубликовали Часть 2 и Часть 3.

Примечание (3): При написании данного пособия 2 февраля, 2015г., Solidity все еще находился в стадии разработки, это означает, что запуск абсолютного кода сопровождается трудностями, и, пока он находится в разработке, его нельзя считать стабильным.

Примечание (4): С течением времени, по мере того, как Solidity и системы смарт-контрактов будут меняться, данная публикация неизбежно устареет. Просим вас заглядывать в наш информационный архив, где мы будем хранить обновленные ресурсы, полные тексты и контракты.

Примечание (5) Я все равно выбираю Solidity, т.к. он становится следующим крупным языком, и он достаточно чист, чтобы стать хорошим «псевдокодом».

Данная статья является первой частью (Часть 1) серии, состоящей из 6 частей.

Введение

Это введение в системы смарт-контрактов. Цель данного документа – научить методам написания крупных, масштабируемых интерфейсов контрактов для распределенных приложений. Читателям должны быть знакомы основы написания смарт-контрактов, и они должны знать, что такое аккаунты, контракты и транзакции, и как с ними работать. Хорошее (и обязательное для прочтения) введение в разработку смарт-контракта можно найти здесь.

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

Кроме этого, я бы также порекомендовал ознакомиться с информацией на Ethereum wiki. На данном ресурсе имеются привязки к вышеупомянутым документам, а также множество другой информации, например ABI интерфейс контракта и национальные технические условия (для документации). Для обсуждения конкретных реализаций Thelonious, можно обратиться к команде Eris на #erisindustries в Свободной ноде или на наш собственный ресурс   subreddit, /r/ErisIndustries

О доверии: Системы, которые мы здесь рассматриваем, разработаны, чтобы быть модульными, т.е. части кода можно замещать во время выполнения, что, в свою очередь, делает их надежными по своей природе. Кто-то должен получить разрешение делать данные обновления. Это необходимо знать. Если вы хотите научиться писать небольшие, ненадежные автоматические системы, ты вам не сюда (хотя многие принципы аналогичны в обоих видах систем).

Смарт-контракты как сервисы

Одно из мнений о смарт-контрактах, и именно таким образом мы собираемся о них думать, это то, что они являются исключительно основными, не имеющими состояния вэб-сервисами. Вэб-сервисы – это единицы функционала в системе (интернете), с хорошо определенным API и идентификатором (IP-адресом), которые можно использовать для их вызова. Одновременно, смарт-контракты являются единицей функционала, функции, демонстрируемые их Solidity-контрактами, будут (при удаленном вызове процедур) API, а их публичный адрес – это идентификатор. Вэб-сервис обычно вызывается созданием запроса http, а контракт вызывается созданием транзакции. Также, в большинстве случаев всем позволяется их вызывать, терминалы вызова общедоступны, и это также относится к контрактам и их функциям. Мы даже можем использовать образцы и архитектуры, например, такие как архитектура микросервисов.

В итоге, до начала работы, необходимо понять следующее: Написание смарт-контрактов может быть коварным. Среда, в которой запускается код смарт-контрактов, отличается от среды нормального кода. Аналогия с вэб-сервисами хороша, т.к. это делает смарт-контракты и системы смарт-контрактов более осязаемыми, и упрощает использование уже существующих моделей и инструментов при работе с ними, но написание абсолютного кода все равно является трудным.

Простой смартконтракт

Данный документ посвящен системам смарт-контрактов, но мы начнем с рассматривания единичных контрактов. Например, стандартный контракт реестра имен. Контракты реестра имен (или “namereg”-contracts) обычно позволяет людям связывать имя с адресом аккаунта нового пользователя. Вот пример такого контракта:

Когда вызывается этот контракт, он в качестве параметров использует msg.sender и предоставленное имя name. msg.sender ссылается на адреса аккаунта, который произвел транзакцию. name  — это последовательность фиксированной длины, которую отправитель включает в данные транзакции. Если имя уже занято, это будет написано пользователям.

Это очень примитивный, но полезный контракт. Он позволяет вам ссылаться обращаться к пользователям по имени вместо использования их публичных адресов. Его можно использовать в качестве основы практически для всего. Он может использовать некоторый дополнительный функционал, например способность вносить в список всех зарегистрированных пользователей, и, возможно, также возможность получать имя по имеющемуся адресу, а не только адрес по имени, а также другие вещи. Мы не собираемся здесь изучать контракты реестра имен, хотя мы собираемся изучать системы, т.о. мы начнем, вместо этого, с другого контракта:

Использование и удаление контрактов

Контракт HelloSystem можно без проблем использовать как есть, без внесения каких-либо изменений, но после использования он навсегда останется в чейне. Нам необходим способ для его удаления. В Solidity командой для удаления (или самоуничтожения) контракта будет следующая:suicide(addr). Аргументом здесь является адрес, на который должны быть отправлены все оставшиеся средства. Чтобы продемонстрировать данный функционал, нам необходимо разместить его внутри (косвенно публичной) функции. Вот как могла бы выглядеть функция самоуничтожения в HelloSystem:

Она удалит контракт после вызова функции remove , и вернет все имеющиеся средства вызывающему. Не стоит говорить, что это не идеально. Обычно, при добавлении функции самоуничтожения вы хотите ограничить к ней доступ. Самый простой способ это сделать – хранить адрес производителя контракта при использовании контракта, и позволять уничтожить его только производителю. Вот как это можно воплотить:

Имейте в виду, что отправители в конструкторе и функции удаления различаются. Конструктор вызывается при добавлении контракта, т.о. msg.sender будет содержать адрес создателя контракта, тогда как во всех других функциях это значение будет адресом вызывающего его аккаунта.

Есть несколько различных способов контролировать добавление и удаление контрактов. Пользователи могут создавать их, осуществляя транзакции создания клиенту. Другой способ – заставить их создавать сами контракты. Контрактам разрешается создавать другие контракты. Вот пример контракта, создающего HelloSystem.

Обратите внимание, что здесь произошло. Мы создаем новый контракт, но мы не добавляем его в отображение или другой параметр, вместо этого мы просто создаем его и передаем его адрес назад вызывающему. Нам необходима функция deleteHS , т.к. производителем всех контрактов HelloSystem является HelloFactory, что означает, что HelloFactory является единственным контрактом (или аккаунтом), которому разрешается их удалять.

Разрешения аккаунтов и зависимости контракта

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

1) Зависимости в условиях кода 2) Зависимости в условиях разрешений

Если мы обратимся к HelloFactory , мы можем видеть, что HelloSystem  — это зависимость, т.к. HelloFactory вызывает функции на данный контракт. С другой стороны, HelloSystem, не нуждается в вызове функций на  HelloFactory. Что касается разрешений, то здесь все наоборот; HelloFactory позволит вызовы с любого акаунта, тогда как HelloSystem принимает только вызовы, произведенные с одного единственого аккаунта, а именно с аканта его владельца (который в этом случае является аккаунтом контракта HelloFactory).

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

Простая банковская система

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

Этот контракт позволит другим аккаунтам переводить и снимать балансы токенов, но он не очень хорошо масштабирует. Предположим, мы запускаем приложение DApp, использующее данный контракт. В конечном итоге у нас может быть много пользователей, и, вероятно, они начнут запрашивать новые характеристики. Возможно у них будут другие средства (подобные основанным на смарт-контрактах альткоинах), и они захотят хранить все в одном месте. Мы можем просто расширить интерфейс пользователя, чтобы также указывать на эти дополнительные контракты, но все будет не связано между собой. Людям придется использовать много имен пользователя, много аккаунтов, и. т.д. В какой-то момент мы вероятно захотим добавить в контракты некоторую логику. К сожалению, нашим пользователи все еще придется вызвать банковский контракт напрямую, т.к. он проверяет адрес вызывающего как в функции deposit , так и в функции withdraw , что означает, что мы фактически не можем использовать прокси-аккаунт. Другая проблема заключается в том, что функции не имеют возвращаемых значений, т.о. контракт, который вызывает банк, не может в действительности знать, успешен ли его вызов.

Если мы хотим, чтобы банковский контракт больше подходил для системы, мы могли бы изменить его в нечто вроде этого:

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

Сейчас банковские операции могут осуществляться фондовым менеджером. Можно передавать транзакции фондовому менеджеру вместо банковского контракта. Это хорошо, т.к. это добавляет разделение функций, и позволяет нам добавлять дополнительные проверки безопасности и другие вещи в контракт фондового менеджера, которые выполняются до вызова абсолютного банковского контракта (по крайней мере до тех пор, пока мы не убедимся, что только фондовый менеджер имеет доступ к банковскому функционалу, что мы сделаем позднее). Однако, система не является очень модульной, потому что мы придерживаемся определенного банковского контракта. Что если мы захотим обновить сам банковский контракт?

Здесь мы будем заниматься работой с интерфейсом вместо того, чтобы позволить банковскому аккаунту быть замененным:

Эта система лучше, но она не достаточно гибкая; и не безопасная. Прежде всего, мы только позволяем владельцу настроить банк. Для получения разрешений нам, возможно, понадобится более сложная система. Конечно, нам также необходимо защитить функции банковского контракта, т.о. они становятся доступны только для фондового менеджера.

Больше управления разрешениями

Во-первых, добавим несколько простых уровней разрешений пользователя фондовому менеджеру. Мы всего лишь используем значение 0 для отсутствия разрешений, и 1 для имеющихся на данном этапе банковских разрешений, но мы используем uint вместо bool , чтобы позднее мы могли его растянуть. Мы также сделаем возможным настройку владельца банковского контракта, так чтобы мы могли настроить адрес владельца в любое время взамен его автоматического назначения во время использования контракта.

Сейчас у нас имеется система, аналогичная HelloFactory и HelloSystem. Мы даже можем составить график основных зависимостей.

Тем не менее, предстоит еще много работы. Мы не обеспечили оперирование системы со множеством различных типов банков, с чего следовало бы начать. Также, мы не рассмотрели последствия отключения банковского аккаунта. Какие из данных останутся? Мы не можем просто отключить аккаунт и создать новый банк, нам необходимо будет каким-то образом объединить старые балансы в один новый контракт. Также нам придется убедиться в том, что мы перемещаем актуальные Ether (эфиры), хранящиеся в старом контракте. Либо так, либо удостовериться, что новый банк получает разрешения совершать операции на старом аккаунте, и делает это как часть своего функционала.

То, что мы здесь сделали, очень частая ошибка: мы не отделили базу данных от контроллера. Он верен в банковском контракте, а также в фондовом менеджере; он хранит отображения разрешений в том же контракте, который на нем оперирует. Что если мы захотим обновить систему разрешений? Нам придется полностью заменить весь контракт фондового менеджера.

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

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

Системы смартконтрактовмодель из пяти типов

Во-первых: Каждому нетривиальному приложению DApp для нормального функционирования потребуется более одного контракта. Нельзя написать безопасный и масштабируемый интерфейс смарт-контракта без распределения данных и логики по множеству контрактов. Сложно точно узнать, как это сделать, поэтому мы начнем с разделения контрактов на категории; вместо того, чтобы думать о них с точки зрения того, что они делают, мы начнем думать о них с точки зрения того, что они собой представляют. Существует много различных способов классификации контрактов, но мы будем использовать тот, который я называю «модель пяти типов». Это простая модель, в которой контракты делятся на пять основных категорий:

1) Контракты базы данных

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

2) Контракты контроллера

Эти контракты действуют на контрактах хранения. В гибкой системе и контроллеры, и базы данных можно заменять другими, подобными контрактами, которые разделяют публичные приложения api (хотя эт оне всегда необходимо). Контроллеры можно продвигать вперед, и могут, например, выполнять пакетные чтения/написания, или читаться или записываться во множество различных баз данных вместо одной.

3) Контракт, управляющий контрактами (CMCs)

Цель данных контрактов – только управлять другими контрактами. Их основная задача – отслеживать все контракты/компоненты системы, управлять коммуникациями между данными компонентами и упрощать модульную структуру. Отделение данного функционала от логики обычного бизнеса следует считать хорошей практикой, и имеет ряд позитивных моментов для системы (как мы увидим позднее).

4) Контракты прикладной логики (ALCs)

Контракты прикладной логики содержат специальные коды приложения. Проще говоря, если контракт использует контроллеры и другие контракты для выполнения специальных задач приложения, это контракт прикладной логики ALC.

5) Контракты использования

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

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

Система фондового менеджмента – дубль 2

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

Обратите внимание, как работают разрешения. Банк не использует контракт разрешений; он все еще может использоваться только контрактом FundManager. Проверки разрешений будут выполняться, как раньше, кроме того, что код будет храниться в отдельном контракте (или, точнее, в двух контрактах). Доступ к двум базам данных может обеспечиваться только их контроллерами, а к контроллерам – только менеджером финансов. Менеджер финансов, в свою очередь, делает то, что ему приказывает пользователь, т.е. он не является автономным. То же самое относится к контроллерам.

Также данный график разрешений не является полным. Прежде всего, владельцем также может быть и пользователь, и ему может быть разрешено вести банковские операции. Во-вторых, фактически здесь мы имеем два вида разрешений, банковское и административное (добавление и удаление контрактов). Если бы мы захотели сделать это правильно, нам пришлось бы разделить аккаунт каждого контракта на несколько секций, в зависимости от разрешений, необходимых для вызова кода в данном блоке, и использовать один массив для каждого вида разрешения, но мы не будем этого делать.

Добавление CMC

Сейчас нам необходимо связать это все вместе. Нам нужно удостовериться, что две пары контроллербаза данных находят друг друга, и что менеджер финансов находит двух контролеров, но вместо того. Чтобы придерживаться данного вида логики в самих контрактах, мы нарушим его и поместим в контракты CMC. Если бы мы хотели сделать это правильно, нам, вероятно, следовало бы добавить один контракт CMC для управления потока контроллер-база данных для банка, один для разрешений, и один дополнительный для всей системы в целом, придав ему древовидную структуру, но для простоты мы сгладим вещи и будем продолжать использовать для всего стандартный контракт CMC:

Имейте в виду, что фактически Doug – это неправильный термин. Doug – это не один смарт-контракт, а много, со множеством компонентов. Один из компонентов – регистрация имени, т.о. я склонен вызвать данные типы высокоуровневых контрактов регистрации имен CMCs Doug.

Мы будем использовать данный контракт для хранения следующих контрактов: “fundmanager”, “bank”, “bankdb”, “perms”, “permsdb”. Мы собираемся также добавить ссылки на Doug в каждый из них, а затем использовать его в качесвте связующего элемента. Они вызовут doug , чтобы получить адрес для необходимых им контрактов, подсчитают их, а затем вызовут функции. Вот как это будет работать более детально:

Все контракты, являющиеся частью данной системы, будут расширять контракт DougEnabled , это будет выглядеть следующим образом:

Данная логика будет использоваться Doug, когда вызывается контракт addContract , для его настройки. Если у контракта уже имеется адрес Doug, он вернет значение ЛОЖЬ на адрес setDougAddress, и в этом случае он не будет добавляться.

Базы данных вызовут doug, когда что-либо будет пытаться их изменить. Вот как это делает банк:

Контроллеры использовали бы Doug, чтобы проверить и убедиться, что вызывающий клиент является “fundmanager”, и он также использовал бы Doug, чтобы получить адрес соответствующей базы данных и произвести считывания, а менеджер финансов использовал Doug, чтобы получить адрес для банковских контроллеров и контроллеров разрешений. Также, использование данного контракта CMC было бы чем-то вроде навязывания всего в глобальную систему имен, а мы хотим сохранить систему простой. В большинстве систем у вас имелось бы больше одного контракта CMC, а также больше продвинутой CMC логики.

 

Законченные контракты

 

Это все контракты в своей конечной форме.

 

Применение

 

Вот как будет применяться данная система:

  1.  Используйте Doug. 
  2. Используйте все другие контракты (не важно, в каком порядке это выполняется), зарегистрируйте их при помощи Doug
  3. Дополнительно, вызовите финансового менеджера, чтобы установить ваше личное разрешение на 1.

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

 

Расширение системы

 

Если мы хотим добавить больше банков в систему, мы могли бы это сделать. Контракт Doug упрощает добавление и удаление контрактов. Мы могли бы также довольно легко растянуть уже имеющийся код при запущенной системе. Скажем, мы хотим добавить запись в банк. Идея заключается в том, что делается короткая запись в журнале, когда кто-либо осуществляет перечисление или снятие средств. Запись должна содержать тип транзакции (перечисление или снятие), сумму Ether, и временную отметку. Как нам это осуществить?

 

В данной системе это просто. Даже тривиально. Вот как мы могли бы это сделать:

  • Оставьте менеджера финансов без изменений. 
  • Добавьте запись в новый банковский контроллер.
  • Он будет иметь точно такой же код, как текущий контроллер, за исключением того, что он также будет протоколировать.
  •  Зарегистрируйте новый контроллер при помощи Doug под именем “bank
  • Начинайте работу.

 

Мы могли бы фактически добавить лог в качестве отдельного контракта, зарегистрировать его при помощи Doug, и использовать его таким образом. Мы могли бы даже дать ему роль контроллера и базы данных, как и другим контрактам, и, возможно, разрешить использовать его также менеджеру разрешений и другим компонентам.

 

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

 

0 означает отсутствие разрешений.

 

1 означает разрешения пользователя банка.

 

2 означает также разрешения добавлять банковские разрешения к другим.

 

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

 

Можно сделать так, что контракт разрешений будет автоматически применять уровень разрешений 3 к производителю. Это будет так же просто, как добавление данного конструктора в контракт разрешений:

Далее нам следует заменить некоторые из проверок для (msg.sender == owner) на проверки разрешений.

Текущая функция setPermissions находится в фондовом менеджере.

Новая функция

Анализ затрат-выгод

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

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

Здесь также встает вопрос доверия, но я рассматривал его в начале документа.

Далее

Вот план, каким он представляется мне на данный момент.

В части 2 мы рассмотри архитектуру, более общую, чем та, которую мы создали здесь. Я называю ее «архитектурой на основе событий», и она основана архитектуре, которую я использовал для “Народной республики Doug”. Она будет очень скоро опубликована, т.к. большая часть уже написана.

Часть 3 будет содержать анализ примера системы, использующей модель пяти типов. Это маленькая часть, и ее большая часть также уже написана.

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

Часть 5 будет посвящена работе с системами смарт-контрактов, которые управляют обычными вэб-приложениями. Мы рассмотрим нормальное вэб-приложением, использующее приложение RESTful API, и увидим, как можно сформировать интерфейс смарт-контракта, подходящий ко всей системе в целом. Главным образом, это просто вопрос структуризации данных, но он очень важен.

В части 6 мы соберем небольшой комплект инструментов для создания систем смарт-контрактов. Это будет несколько контрактов для основного использования, написанных на Solidity, немного cheetsheats, более развернутой модели, чем «пять типов» для анализа, а также лучшей системы для создания разрешений/составления зависимостей.

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

Теги

Блокчейн глазами IBM: зачем нужен проект HyperLedger и когда мир перейдёт на новую технологию В четверг, 10 ноября, IT-гигант IBM представит своё видение того,…
admin by admin
0 8588 0
Взгляд Microsoft на блокчейн-технологию: от слов к коду Представляем одного из ключевых спикеров Blockchain & Bitcoin Conference Russia…
admin by admin
0 4910 0
Блокчейн в банковской системе: взгляд Сбербанка Сбербанк России – один из лидеров банковского рынка по внедрению…
admin by admin
0 5948 0

Leave a Reply

Войти
Регистрация
Отправить сообщение