Код смарт контракта ethereum

Обновляемые смарт-контракты в сети Ethereum

Мотивация

Контракты сети Ethereum иммутабельны – единожды загруженные в сети (блокчейн), они не могут быть изменены. Специфика бизнеса или разработки могут потребовать обновить код, но при традиционном подходе это становится проблемой.

Популярные причины необходимости обновления

  • Ошибки в коде
  • Изменение бизнес требований
  • Принятие предложений сообщества об изменении работы контракта

Описание технического решения

Реализация требуемого функционала — обновление кода, планируется через разделение кода на составляющие:

  1. Данные — смарт-контракты без логики и предоставляющие исключительно пространство для хранения данных;
  2. Бизнес-логика — смарт-контракты описывающие логику извлечения данных из хранилища и их изменения;
  3. Входные точки — иммутабельные контракты ведут учет обновления бизнес-логики и предоставляют конечному пользователю ссылку на актуальный контракт бизнес-логики

Обновляемый смарт-контракт счетчика

Представим абстрактный оторванный от реальности пример – счетчик с обновляемой логикой увеличения.

  • Стадия 1. С каждым вызовом счетчик увеличивается на 1
  • Стадия 2. С каждым вызовом счетчик увеличивается на 10

При традиционном подходе и изначальном знании о всех стадиях, было бы необходимо сделать в счетчике поле явно указывающее текущую стадию, например: uint public currentState. При каждом вызове метода увеличения счетчика происходила бы проверка текущей стадии и выполнялся код ассоциированной с ней:

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

Хранилище

Для реализации слоя данных хранящего текущее значение счетчика и отделенного от бизнес-логики, создаем контракт –

Как видно из названия и реализации контракта – хранилище ничего не знает о том как его будут использовать и выполняет задачу инкапсуляции поля uint private value

Бизнес-логика

Договоримся, что взаимодействие с нашей бизнес-логикой будет осуществляться через два метода: increaseCounter и getCounter для увеличения счетчика и получения текущего значения соответственно, о чем явно опишем в интерфейсе –

Далее опишем смарт-контракт бизнес-логики из первой стадии реализующий ICounter интерфейс и использующий ранее описанное хранилище –

Важно отметить, что IncrementCounter не имеет внутреннего состояния (не хранит данные), кроме ссылки на хранилище.

Если договориться передавать в метод increaseCounter и getCounter ссылку на хранилище первым аргуметом, можно реализовать стейт-лесс бизнес-логику

Вносим изменения в

Теперь методы бизнес-логики ждут первым агрументом ссылку на хранилище, а так же реализуют метод проверки хранилища на валидность: validateStorage(address _storage)

Внесем изменения в реализацию первой стадии –

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

Тестирование

Данный репозиторий является проектом фреймворка Truffle и предоставляет удобный функционал для тестирования: truffle test .

Я не буду подробно описывать процесс написания тестов, но если эта тема вам интересна – напишите мне в телеграм @alerdenisov и я подготовлю статью с best-practice тестирования контрактов.

Запуск тестов покажет, что все «прекрасно»:

Но на самом деле это не так. Допишем промежуточный тест «неавторизированного» взаимодействия с хранилищем:

Владение хранилищем

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

Читайте также:  Международные финансовые организации иностранные инвестиции

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

Задача сделать так, чтобы изменять хранилище мог исключительно актуальный контракт бизнес-логики.

Для явного ограничения взаимодействия с хранилищем, воспользуемся паттерном Ownable из фреймворка zeppelin-solidity (подробнее c паттерном можно ознакомиться в документации к фреймворку).

Наследуем хранилище от Ownable контракта и добавим модификатор onlyOwner на метод setValue() :

Поздравляю, теперь в наше хранилище может писать только ассоциированный владелец хранилища! Теперь уже 3 из 6 теста проваливаются! Давайте в тестах «в ручную» передадим бизнес-логики управление хранилищем:

Теперь все тесты проходят, но встает второй вопрос: «Как управлять владением хранилища при обновлении бизнес-логики»

Общий контроллер

Перед реализацией общего контроллера сделаем еще один контракт счетчика, но уже второй стадии –

Теперь когда у нас есть две реализации счетчика и Ownable хранилище, становится понятно, что необходимо как-то «просить» одну реализацию отдать другой управление хранилищем. Добавим метод transferStorage(address _storage, address _counter) в интерфейс счетчиков –

Договоримся, что финальная реализация ICounter должна после вызова метода transferStorage отдавать управление хранилищем адресу переданному в параметр _counter :

Давайте допишем тесты передачи прав новой логике и проверим результат increaseCounter метода после смены логики:

Выполнение тестов может дать ложное ощущение, что все работает:

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

Основная задача общего контроллера будет управлять передачей прав и не допускать кого-угодно к этому процессу. Сначала изменим IncrementCounter по аналогии с UIntStorage , чтобы он тоже наследовал логику Ownable и ограничивал взаимодействие с хранилищем:

Приступим к реализации контроллера. Основные требования к контроллеру:
1) Учет текущей реализации счетчика
2) Обновление реализации счетчика
2) Перемещение прав на хранилище при обновлении реализации
3) Отклонение попыток неавторизированного обновление реализации

increaseCounter и getCounter не более, чем просто внешние методы взаимодействия с аналогичными в текущей реализации ICounter . Вся логика контроллера находится в небольшом методе: updateCounter(address _counter) .

Метод updateCounter принимает адресс на реализацию счетчика и перед установкой его как адреса новой реализации счетчика? передает ему права на хранилище (от себя или от предыдущей в зависимости от состояния).

Помните про третью стадию? Я опущу код ее реализации, тем более, что отличается от второй только одной строчкой. Просто скажу, что в третьей стадии счетчик будет увеличивать значение умножением на самого себя: value = value * value .

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

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

Резюме

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

Но у данного подхода есть ряд существенных недостатков и комментариев:

  • Увеличивается стоимость транзакций (объем потребляемого газа), но не значительно. Если есть желающие провести подсчеты – буду признателен или ожидайте в ближайшем будущем от меня.
  • Появляется роль администратора, но решается передачей прав на контроллер смарт-контракту децентрализованного голосования за принятие обносвлений
  • Сложность проектирования, писать код в одном монолитном контексте в разы проще и требует меньше внимания к потокам данных и сообщений. Реализация state-less требует еще большего внимания от разработчика. Решается вызовом реализации через delegatecall . Напишите мне если нужно написать продолжение с передачей состояния через delegatecall .
Читайте также:  Фермы для выращивания криптовалют

Источник

Создаем первый смарт-контракт на Ethereum менее чем за 60 минут

Расскажем, как создать смарт-контракт на блокчейне Ethereum при помощи сервера Ganache и программы на ЯП смарт-контрактов Solidity.

Смарт-контракт: общие сведения

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

Для этого создается особый программный объект – смарт-контракт. Такие программы записываются в блокчейн и запоминаются навсегда. У всех участников сети есть ее копия. При этом работу контракта можно совместить с управлением денежными операциями: созданием аукциона, пари, лотереи, игры с денежным вознаграждением и т. д.

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

Рассмотрим реализацию смарт-контракта при помощи блокчейна Ethereum. Смарт-контракты Bitcoin ограничены в возможностях, в то время как Ethereum был спроектирован с учетом этих потребностей, и активно применяется для распределенной работы программ на основе блокчейн технологии, в частности, виртуальной машины Turing Complete.

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

Предварительные настройки: локальный блокчейн Ganache и MyEtherWallet

Первым делом необходимо установить Ganache — инструмент для создания приватного блокчейна, работающего на вашем компьютере. Советуем загрузить версию 1.0.2, так как последняя версия по нашему опыту пока некорректно обрабатывает запросы смарт-контрактов. Также вам понадобится локальная копия MyEtherWallet (url-сайт на в рассматриваемом случае не подойдет). По последней ссылке вам нужно загрузить пакет с названием вида etherwallet-v3.xx.x.x.zip.

Смарт-контракт: написание и компиляция кода в Remix

После того как у вас имеется все необходимое, переходим на сайт https://remix.ethereum.org/ и видим следующий экран онлайн-IDE Remix.

Онлайн-редактор Remix содержит компилятор для языка Solidity. При первом посещении страницы редактор для наглядности загружается с некоторым кодом. Этот код можно безболезненно стереть и заменить программой контракта Counter:

Он содержит одну переменную count и три функции. Переменная представляет собой целое число и является локальной – доступна только в пределах контракта Counter. Первая и вторая функции incrementCounter() и decrementCounter() изменяют значение count : повышают или понижают на единицу. Третья функция getCount() возвращает текущее значение переменной count .

При переносе кода в Remix он будет автоматически скомпилирован (по умолчанию на сайте стоит галочка Auto compile).

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

Запуск сервера блокчейна

Запустите предварительно установленное приложение Ganache. Обратите внимание на ссылку на RPC SERVER. Она также нам вскоре понадобится.

Разархивируйте сохраненную ранее zip-папку MyEtherWallet и откройте index.html в веб-браузере. В правом верхнем углу находится кнопка с надписью Network ETH. По умолчанию в качестве сети выбирается основная сеть Ethereum (mainnet). Для изменения этого параметра, в выпадающем списке выбираем Add Custom Network / Node.

При этом откроется окно, в котором вы можете вставить информацию о RPC Server, предоставленную Ganache. Имя узла (Node Name) можно выбрать произвольное.

Читайте также:  1660 super энергопотребление майнинг

С этого момента локальная копия MyEtherWallet подключится к вашему блокчейн серверу на Ganache.

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

Воспользуемся MyEtherWallet, чтобы загрузить смарт-контракт Counter. Для этого в верхней навигационной панели нажимаем на Contracts и на открывшейся странице контрактов выбираем Deploy Contract.

Видим, что MyEtherWallet запрашивает байт-код контракта. Возвращаемся в Remix IDE и нажимаем кнопку “Details”.

При этом появится окно со множеством деталей о контракте Counter. Для того, чтобы скомпилировать байт-код, скопируйте его из параметра object раздела BYTECODE (удобнее всего скопировать весь блок при помощи соответствующей иконки, а потом удалить все лишнее).

Переносим байт-код в MyEtherWallet. Параметр Gas Limit определится автоматически.

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

Копируем ключ, выбираем Private Key и вставляем значение

Теперь, если нажать Unlock, MyEtherWallet попросит нас подтвердить транзакцию.

При нажатии Sign Transaction под кнопкой добавятся Raw Transaction и Signed Transaction. Нажимаем на Deploy Contract.

Соглашаемся с предупреждением.

В результате транзакции в Ganache в разделе текущих обрабатываемых блоков (Current Blocks) число изменится с 0 на 1. Также на 1 возрастет счетчик для выбранного ключа.

Теперь смарт-контракт загружен в локальный блокчейн.

Настройка взаимодействий с контрактом

Для того, чтобы провзаимодействовать со счетчиком контракта, возвращаемся в MyEtherWallet и выбираем раздел Interact With Contract.


Как видим, MyEtherWallet запрашивает адрес контракта и бинарный интерфейс приложения (Application Binary Interface, ABI). Адрес контракта хранится в Ganache, в разделе Transactions.

При выборе соответствующего раздела откроется лог всех проведенных транзакций.

При нажатии на запись о транзакции будет выведена вся информация о ней, среди которой необходимый адрес контракта – Created Contact Address.

Копируем адрес и вставляем в соответствующее поле MyEhtherWallet.

Все, что осталось – это найти ABI – то, что сообщает MyEtherWallet как взаимодействовать с нашим контрактом. Для этого возвращаемя к тому месту, где мы остановились в Remix (Details), в разделе ABI копируем информацию в буфер обмена, переносим в MyEtherWallet и нажимаем Access. При этом появится раздел Read / Write Contract, у которого при помощи выпадающего списка станет доступен выбор функций из тех, что мы записали в нашей программе.

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

Проверим работу смарт-контракта. В нашем коде мы инициализировали значение переменной count нулем. Для того, чтобы подтвердить корректность работы, вызовем функцию getCount() .

То есть функция работает корректно. Функция инкремента предполагает активное действие с нашей стороны – осуществление транзакции. При выборе функции нажимаем Write и соглашаемся с запросом транзакции.

В результате при новом запросе функции getCount видим 1 вместо 0 (и выросшее число транзакций в Ganache).

Аналогичным образом вы можете протестировать поведение функции decrementCount() .

Заключение

Итак, мы запустили блокчейн на нашем компьютере, развернули на нем смарт-контракт и провзаимодействовали с ним. Это очень близко к тому, как на начальном этапе происходит процесс профессиональной разработки смарт-контрактов на Ethereum. Когда вы становитесь готовы к тому, чтобы другие пользователи могли взаимодействовать с вашим смарт-контрактом без использования реальных денег (как в сети mainnet), вы можете загрузить смарт-контракт в Ethereum test network, (testnet).

Источник

Оцените статью