Coin Lab

0 1539 3
Solidity Часть 2: Архитектура, управляемая действиями

Часть 2: Архитектура, управляемая действиями

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

Примечание (1): Это Часть 2 пособия из шести частей. Часть 1 можно найти здесь. Часть 3 можно найти здесь.

Примечание (2): Ресурсы Части 1 доступны в отдельной папке в недавно созданных Eris Industries ресурсах Solidity, вместе с отдельными, запускаемыми контрактами и простым html файлом для тестирования. Это будет доступно также для Части 2 , но, похоже, клиенты испытывают некоторые проблемы PoC перехода, что усложняет проведение финальных тестов.

Примечание (3): та же оговорка, что и в Части 1: эти системы не разработаны, чтобы быть автоматическими и ненадежными, они созданы, чтобы служить в качестве интерфейсов смарт-контрактов для коммерчески-используемых децентрализованных /распределенных приложений.

Данная статья – это Часть 2 пособия из 6 частей.

Введение

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

Контракты взаимодействуют друг с другом при помощи контракта Doug. Вот иллюстрация нормальной последовательности вызовов:

У этих системами, тем не менее, имеется проблема. Скажем, у нас есть система с 10 контроллерами и 10 базами данных. Каждый контроллер имеет в среднем 4 функции в своем публичном приложении. Что бы это значило? Это означает, что нам необходимо добавить 40 (!) функций в ALC (Контракт прикладной логики), чтобы иметь доступ к каждому из них. И это еще не все. Каждый раз, когда мы обновляем систему и добавляем новые контроллеры или изменяем существующие, нам необходимо выгружать весь контракт ALC!

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

Система, которую я собираюсь здесь предложить, позволит вам сохранить одну точку входа и отключать отдельные функции, не затрагивая другие коды, она полностью устраняет необходимость в контроллерах, она масштабируемая, безопасная, в ее основе лежит массивная система proofofconcept Народная республика Doug, или PRODOUG. PRODOUG – это управляемое смарт-контрактами приложение, которое работает аналогично правительству, имеет банковскую систему, гражданство /право на имущество, земельный кадастр, голосование, форум и некоторые другие вещи. В общей сложности приложение имеет около 70 уникальных контрактов, которые созданы из примерно 20 К-линий кода контракта LLL, и изначально оно запускается на тестовых чейнах раннего Ethereum.

TLDR

Хорошее, гибкое управление разрешениями. Одна единица функциональностиодно событиеодно разрешение. Контракт не очень хороший разделитель, т.к. для удаления контракта и выполнения одной из его основных функций (например, перечисление средств на аккаунт) потребуются совершенно другие уровни разрешений. Действия лучше.

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

Полностью расширяемый. Можно добавлять и удалять отдельные действия, не влияя на остальную систему.

Действия

PRODOUG сделали безопасным путем инкапсуляции всех кодов, которые могут запускать пользователи в процессах, называемых действиями. Все входящие транзакции должны быть в особом формате, и должны отправляться при помощи контракта «менеджера действий» (с небольшими исключениями). Вот пример супер простого интерфейса действия:

Способ, при помощи которого вы управляете и вызываете контракты действий, — это хранение ссылок на них в контракте менеджера:

Важно

Т.к. мы должны допускать обобщенный аргумент, то нам необходимо превратить что-либо в действие, которое может замещать любое количество аргументов любого типа – как Object в java, interface{} в Go, или *void в C. Это не поддерживается полностью в Solidity, но первое, что будет полезным в этом случае, это, скорее всего, байтовые массивы – именно так это работало в LLL. Полная поддержка для использования байтовых массивов таким образом реализуется в данный момент. В целом, байтовые массивы полностью обобщены, т.о. здесь (пока) мы будем использовать библиотеку javascript (например, web3), которая упрощает конвертацию аргументов в надлежащим образом отформатированные данные о вызове. Имеются также другие решения, например, эта Модулярная функция.

В качестве примера того, что мы хотим сделать, давайте представим, что вы косвенно вызываете контракт действия (через менеджера действий), которому требуется две единицы uint. Вы не можете провести их через менеджера действий без изменений, т.к. функция execute менеджера действий должна иметь возможность передавать любой набор параметров своим действиям – не только две единицы uint. Мы попросту упакуем две единицы в байтовый массив в нашем коде javascript, а затем заставим функцию execute нашего контракта действий принять единичный bytes объект в качестве входных данных. В таком случае действия будут отвечать за чтение байтов и их отбор – в этом случае первые 32 байта будут отобраны в  uint, и следующие 32 байта тоже. Вызов действия внутри менеджера действий будет выглядеть следующим образом: Action(theActionAddress).execute(params); т.к. мы могли добавить функцию execute с одним параметром bytes в класс базы событий, т.к. подпись будет одинакова для всех действий.

Другой способ – добавление байтового массива, содержащего параметры с 4-байтовым идентификатором функции выполнения, что означает, что мы создаем законченный массив вызова данных, а не просто параметры. Это так же просто с  web3. Когда мы делаем обобщенный вызов call внутри функции execute в контрактах действий (в данном примере это будет function execute(uint someName, uint someOtherName)), что не только выглядит лучше, но также и означает, что мы получаем из этого надлежащий интерфейс json ABI.

В части 3 будет больше информации о javascript и утилитах. Намного больше.

Контроллер и база данных

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

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

Конечно, пока еще нет настоящей защиты. На данном этапе мы имеем простую систему действий. Люди могут добавлять в нее действия, удалять и выполнять их. До того как мы сможем добавить в нее какие-либо действия, нам необходимо добавить другой компонент — Doug. Даже если технически менеджер действий является частью контракта CMC (контракт, управляющий контрактом), нам все равно понадобится Doug. Он будет связывать действия и менеджера действий с другими контрактами в системе, например, с базами данных. Начнем с типа реестра имен Doug, подобного описанному в части 1.

Мы также добавим суперпростой банк, или кредитный контракт.

Вот как будет инициализироваться система:

1) Используйте контракт Doug. 2) Используйте контракт менеджера действий и зарегистрируйте его под именем «действия». 3) Используйте банковский контракт и зарегистрируйте его вместе с контрактом Doug под именем «банк».

Далее нам необходимо добавить одно действие для обеспечения адреса монетами, и одно для сбора монет. Однако нам нужно добавить еще одну функцию в интерфейс действий – функцию setDougAddress. Именно эта функция обеспечит действиям (косвенный) доступ ко всем контрактам в системе, так чтобы они могли выполнять свою работу. Это также важная мера безопасности. Мы будем использовать контракт DougEnabled из части 1.

Основной шаблон действий запустится следующим образом:

Помните, что мы не включаем функцию «выполнения» по причинам, упомянутым ранее. Мы добавим функцию выполнения для каждого действия.

 

Чтобы заблокировать данные контракты, мы просто позволим контракту, зарегистрированному сейчас как actions , вызвать функции. Очень похоже на контракт FundManagerEnabled в части 1.

Вот новый базовый класс действия:

Вот контракт обеспечения действия.

Это действие для тарификации.

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

Разрешения

Этап 2 – это контроль доступа к Doug извне. Взаимодействие с контрактами должно быть возможно только через действия, а запускать действия должно быть возможным только через менеджера действий. Первое, что необходимо сделать, это убедиться, что менеджер действий (а позднее и база данных) вызывает функцию ‘setDougAddress’, которую мы добавили к действиям. Он должен вызвать ее и передать адрес DOUG действию сразу же после регистрации. Если функция отвечает ЛОЖЬ, это означает, что у нее уже есть набор адресов doug, что, в свою очередь, означает, что действие вообще не нужно регистрировать менеджером действий. Это небезопасно.

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

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

Обновленные контракты

Вот новый менеджер действий. Мы добавляем функционал setDougAddress при добавлении действий, а также поле «активного контракта», которое будет использоваться для валидации. Сейчас мы сделаем ActionDb вызываемым только из менеджера действий, но позднее у нас будет даже лучшая система.

Вот новый банк:

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

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

Блокировка (фиксирование) Locking things down

Последнее, что нам нужно сделать – доступ к DOUG и менеджеру действий. Праивльно, что банк и другие контракты должны вызываться через действия, но каждый имеет разрешение добавлять и удалять контракты из DOUG. Мы начнем с добавления простых разрешений для акаунтов. Это будет зарегистрировано при помощи DOUG под именем “perms”. Затем мы добавим функции для действий, в которых можно получать и устанавливать разрешения. В конце мы укомплектуем систему следующими основными действиями:

  • добавить действие

  • удалить действие

  • добавить контракт

  • удалить контракт

  • установить разрешения аккаунта

  • изменить разрешения действия

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

Совет: Не удаляйте действие добавления действия.

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

Вот как мы обновим функцию выполнения менеджеров действий.

Двунаправленный связный список

До того как мы перейдем к формированию конечных контрактов, нам необходимо обратиться к одной важной вещи, которую мы еще не затрагивали. Если посмотреть на Doug, или базу данных действий, или любой контракт базы данных по данному предмету, что плохое есть у всех них? Тот факт, что у нас нет способа сбора всех входов в отображения. Нам необходимо получить входы (например, контракты, в случае Doug) ключом. Тип отображения mapping , который поддерживает все эти базы данных, не имеет встроенного итератора или функции получения всех элементов. Один из способов добавления данных характеристик в отображение состоит в помещении их внутрь структуры данных связного списка.

Двунаправленный связный список над отображением mapping дает большие преимущества. Мы можем динамично добавлять и удалять элементы. Мы можем получать элементы ключом. Все эти операции являются O(1) , т.о. это недорого в отношении вычислений. Недостатком является то, что в хранилище добавляются дополнительные данные, что немаловажно.

Итак, что же нам необходимо добавить?

Этап 1

Сначала нам нужно добавить три дополнительных поля к контракту – размер списка и ссылки на текущие начало и конец. Давайте начнем с «обобщенного» контракта связного списка, использующего адреса в качестве ключей, и последовательности фиксированной длины в качестве значения.

Этап 2

Чтобы сохранить ссылки на предыдущий и следующий элементы, нам необходимо отключить 32-байтовое значение структурой следующим образом:

contract DoublyLinkedList {

struct Element {
address previous;
address next;

bytes32 data;
}

uint size;
address tail;
address head;
mapping(address => Element) elements;
}

Этап 3

Сейчас нам необходимо применить логику для добавления и удаления элементов. Начнем с добавления. Добавим элементы в качестве нового начала head, а добавить логики для элемента легко: Список либо пустой, что означает, что новый элемент станет и началом, и концом, либо он не пустой, тогда он становится новым началом.

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

Добавить новый элемент в качестве следующего элемента текущего начала, и добавить текущее начало в качестве предыдущего элемента нового элемента.

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

co

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

Вариант 1 — когда элемент, который мы удаляем, единственный элемент в списке. В том случае нам необходимо установить и начало, и конец на нулевое значение, установить размер на 0, и удалить сами данные элемента.

Вариант 2 — когда элемент является началом списка. Это означает, что нам необходимо только изменить поле начала, и только следующее поле одного элемента – а именно, элемента, который является предыдущим для текущего элемента.

Вариант 3 – когда элемент – это конец списка, в данном варианте он аналогичен.

И, наконец, вариант 4 — когда данный элемент не является ни началом, ни концом. В этом случае поля начала и конца не будут затрагиваться, но нам нужно связать «вокруг» данный элемент, изменив предыдущий следующего данного элемента, а следующийпредыдущим данного элемента. Вот конечный контракт с функцией добавления и удаления. Мы также добавляем особый аксессор, который получает только данные data, а не весь элемент полностью.

c

Вот все, что нам необходимо для базовой реализации.

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

Помните, что доступ к данным элементам по индексу немного коряв. Лично я использую приложение json ABI для генерации объектов возращенных данных с надлежащими именами и т.д., применяя что-то вроде этого (что также будет в Eris js):

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

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

Связывание

Перед формированием списка конечных контрактов нам необходимо выполнить некоторые изменения.

Doug нужно будет изменить. Нам необходимо валидировать аккаунт, когда кто-то пытается добавить контракт. Это немного странно, т.к. как тогда вы будете добавлять контракт менеджера действий? Один из способов – проверить, был ли добавлен менеджер действий. Если менеджера действий нет, то можно разрешить все. Добавление менеджера действий делается для блокировки системы. А как быть с удалением? Как нам удалять Doug? Кому бы мы не разрешили это сделать, он может убить всю систему одним нажатием кнопки, т.о., это необходимо как-то регулировать, но если это обычное приложение dapp, имеющее владельца, это может быть так же просто, как дать владельцу эксклюзивное право убить контракт DOUG. Не обязательно, чтобы это было одинаково во всех системах.

Помните, что это просто базовая архитектура, управляемая действиями. PRODOUG, к примеру, имел голосование. Т.е. действия могли иногда не выполняться напрямую, вместо этого действие создавало свою копию, и хранилось во временном списке до проведения голосования. Данные типы действий имели функцию инициализации, в которой устанавливались все параметры, и, затем, функцию выполнения, которая выполнялась после окончания голосования. Способ, которым она работала с разрешениями, заключался в том, что на запрос разрешения действия отправляли не номер, а название типа опроса. Иногда опросы были автоматическими (основаны на каком-либо качестве пользователя), а иногда проходило полное голосование с ограничениями во времени, кворумом и т.п. Теперь, оглядываясь назад, я думаю, было бы лучше позволить этим видам действий просто хранить внутренние данные в индексированном списке, отслеживать, какие данные принадлежат какому вызывающему, пока не будет проведено голосование. СОЗДАНИЕ вызовов является очень дорогим в чейнах gas, поэтому короткие действующие объекты (полтергейсты) должны храниться в минимальном количестве.

В итоге, данную систему все еще немного портит система низких уровней, из которой она вышла. Думаете, некоторые объекты и функционал можно сделать лучше? В этом случае, почему бы вам не связаться с нами в Eris. Мы принадлежим к развивающемуся сообществу, и только что выпустили Программу образования и просвещения (Educations and Outreach program). Конечно, многое можно сделать, чтобы расширить эту базовую систему. Скорее всего, позднее я напишу новую подсистему консенсуса, когда у нас будет больше времени для обсуждения и работы с такими вещами. Также не трудно позволить действиям запускать другие действия. Это можно сделать, например, заменяя активное поле действия набором некоторых описаний.

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

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

Чистые интерфейсы

Базовые контракты

База данных действий (ActionDb)

Менеджер действий

Doug

Банк

Разрешения

Действия

Связный список Doug

Далее в программе

В следующей части мы научимся, как объединять системы смарт-контрактов в приложения при помощи вызова удаленных процедур RPC через приложение javascript API. Это требует глубокого рассмотрения ряда важных характеристик solidity, большей частью, events. События необходимы для нахождения результата транзакций, т.к. их можно использовать для печати сообщений в журнале клиента, который можно фиксировать и читать при помощи комбинации filters и watches (кстати, в контракте связного списка Doug есть несколько событий). Это гораздо менее сложно, чем кажется, во многом благодаря инновационной библиотеке web3 javascript.

Будет также и другое, например, исследование блокчейна, которое может быть полезно, чтобы избежать и восстановиться после ошибок. Вызов клиента блокчейна из javascript (или откуда-либо еще) является необходимым I/O, и некоторые базовые знания системы необходимы для правильной работы с ней.

Немного позднее я создам для Eris базовую систему, управляемую действиями, если кому-то захочется попробовать. Она будет включать менеджера действий, Doug, и, для начала, несколько базовых сервисов и действий. Это и несколько систем разрешений и конфигураций, но я признаю, что в большинстве случаев подойдет набор стандартных опций, и их можно сделать доступными. Сюда также будет включено использование javascript и автоматический тестовый сценарий для использования с нашим материалом node.js (уже сделано), и, скорее всего, также сценарии использования.pdx для EPM (менеджер-упаковщик Eris, наш CLI-инструмент использования и управления чейном). Если он «привлечет внимание», то, возможно, для работы с ним также появится простой редактор-администратор, в котором можно будет перечислять действия, добавлять и удалять действия через javascript , и т.д. Все это зависит от того, как заинтересуются люди. Мне самому это нравится, т.к. построение DApp с использованием данной системы будет означать меньшую концентрацию на облике системы, и большую – на фактической бизнес-логике, т.о. я надеюсь, что она будет успешной. Все, что вам нужно сделать, это закинуть несколько базовых типовых контрактов базы данных и действий, и действия станут очень маленькими, благодаря всей логике, которую они автоматически получают из своих основных классов.

Я также собираюсь добавить объекты контракта пользовательского соглашения в форк/расширение web3, с которыми мы работаем, в основном это единичные контракты, которые работают, как обычные, за исключением того, что вызовы делаются в менеджер действий, и они автоматически настраиваются на это. Также, т.к. json abi для контракта дает тип и имя каждого поля, представляется возможным функционирование на данных контрактах для авто-генерирования шаблонов html , которые можно использовать в управлении интерфейсом пользователя. Возможно, появится глобальная опция, по какому типу шаблона использовать, например, простой html, угловой.js и т.д. Это то, к чему стремились мои коллеги в начале работы над «Проектом Douglas», и это даже является частью спецификации c3d. Я здесь работаю не один…

В итоге

Доверяйте разработкам  Solidity и web3 , которые добавляют хорошо-функционирующие и инновационные инструменты в арсенал инструментов написания смарт-контрактов.

Опубликовано Андреасом Олофссоном 12 марта 2015г.  в tutorials

Теги

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

Leave a Reply

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