Глава 8. Разработка Эфириума

Содержание

Глава 8. Разработка Эфириума
Настройка среды
Сеть тестирования (Ropsten)
Установка частной сети
Идентификатор сетевой среды
Порождающий файл
Каталог данных
Флаги и их значение
Статические узлы
Запуск частной сети
Исполнение Mist в частной сети
Оснащение контрактов с помощью Mist
Инструменты и клиенты разработки
Языки
Компиляторы
Solc
IDE (Интегрированные среды разработки)
Браузер Solidity
Ремикс
Установка
Инструменты и библиотеки
Node.js версии 7
Локальный проводник блока Эфириума
EthereumJS
Разработка и развёртывание контракта
Введение в Solidity
Типы
Определяемые значением типы
Булевы
Целые числа
Адреса
Типы значения массива (байтовые массивы фиксированного и динамического размера)
Литералы
Целочисленные литералы
Строковые литералы
Шестнадцатеричные литералы
Перечисления
Типы функций
Внутренние функции
Внешние функции
Cсылочные типы
Массивы
Структуры
Местоположение данных
Соответствия
Глобальные переменные
Структуры управления
События
Наследование
Библиотеки
Функции
Схема файла исходного кода Solidity
Введение Web3
Запросы POST
Интерфейс HTML и JavaScript
Установка web3.js
Пример
Инфраструктуры разработки
Truffle
Установка
Проверка применения truffle
Построение
Другой пример
Пример проекта: Подтверждение идеи
Разрешённые распределённые книги учёта
Выводы

Эта глава вводит основные понятия, технологии и инструменты, относящиеся к разработке Эфириум. В этой главе будут представлены некоторые примеры дополнительно к теоретическим понятиям, приводившимся в предыдущих главах. Эта глава в основном охватывает установку среды разработки и то, как создавать адаптивные контракты (smart contract) с применением блокчейна Эфириум. Будет представлен подробный обход примеров, который поможет вам понять то, как Эфириум и прочие инструменты поддержки могут применяться для разработки и оснащения различных адаптивных контрактов в данном блокчейне.

Настройка среды

Первой важной задачей является установка некоторой среды разработки. Последующие разделы представят установку Эфириум для Тестовой сети и Частной сети. тестовая сеть, имеющая название Ropsten, применяется разработчиками и пользователями в качестве тестовой платформы для проверки адаптивных контрактов и прочих относящихся к блокчейну предложений. Опция Частной сети в Эфириум делает возможным создание независимой частной сети, которая может применяться как распределённая система регистрации между определёнными юридическими лицами, а также для разработки и проверки адаптивных контрактов. Хотя и имеются прочие клиенты в Эфириум, такие как Parity, которые обсуждались в предыдущей главе, geth является ведущим клиентом для Эфириум и обычно выбираемым инструментом, следовательно данная глава будет применять в качестве примеров geth.

Сеть тестирования (Ropsten)

Клиент Эфириум Go, geth, может быть подключён к сетевой среде тестирования при помощи следующей команды:


$ geth --TestNet
		

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

Проводник блокчейна под тестирование размещается по адресу https://testnet.etherscan.io/ и может применяться для отслеживания транзакций и блоков в тестовой сетевой среде Эфириум.

 

Рисунок 8-1


Вывод команды geth, соединяющей Эфириум с тестовой сетевой средой

Установка частной сети

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

  1. Идентификатор сетевой среды.

  2. Файл порождения.

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

Частная сетевая среда делает возможным создание какого- то целиком нового блокчейна. Это отличает её от Тестовой сети и Основной сети в том смысле, что она применяет свой собственный уникальный блок порождения и Идентификатор сети. В основной сети geth имеет сведения об одноранговых узлах (peer) по умолчанию и осуществляют соединение автоматически, однако в Частной сетевой среде geth нуждается в настройке посредством определения соответствующих флагов с тем, чтобы быть способным к обнаружению прочими одноранговыми узлами или отыскивать прочие узлы своего ранга.

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

В последующих разделах подробно обсуждаются все эти параметры в неком практическом примере.

Идентификатор сетевой среды

Идентификатор сети может быть любым положительным числом за исключением 1 и 3, которые, соответственно, уже применяются в Основной сети Эфириум и Тестовой сети (Ropsten). В данном разделе в качестве примера для последующего обсуждения нашего примера частной сети выбран Идентификатор сети с номером 786.

Порождающий файл

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

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


{
  "nonce": "0x0000000000000042",
  "timestamp": "0x0",
  "parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
  "extraData": "0x0",
  "gasLimit": "0x4c4b40",
  "difficulty": "0x400",
  "mixhash":"0x0000000000000000000000000000000000000000000000000000000000000000",
  "coinbase": "0x0000000000000000000000000000000000000000",
  "alloc": { }
}
 	   

Он может быть сохранён как некий текстовый файл с расширением JSON; например, privategenesis.json. Как вариант, может быть предварительно определён адресами выгодополучателей (beneficiary) эфир и общее значение Wei в alloc, однако обычно это не является обязательным, поскольку, будучи определённой как частная сеть, эфир в ней может добываться очень просто.

Каталог данных

Это тот каталог, в котором будут запоминаться данные блокчейна для вашей частной сетевой среды Эфириум. Например, в нашем последующем примере это ~/.ethereum/privatenet.

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

Флаги и их значение

  • --nodiscover: Данный флаг гарантирует то, что этот узел автоматически обнаруживается если так произошло, что он имеет те же самые порождающий файл и Сетевой идентификатор.

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

  • --rpc: Применяется для включения в geth интерфейса RPC.

  • --rpcapi: Данный флаг приводит некий список API, который является допустимым в качестве параметра. Например, eth, web3 включит интерфейс web3 и eth поверх RPC.

  • --rpcport: Устанавливает значение порта TCP RPC, например: 9999.

  • --rpccorsdomain: Этот флаг определяет тот URL, который разрешает подключение к имеющемуся частному узлу geth и выполнять операции RPC.

  • --port: Определяет значение порта TCP, который будет применяться для ожидания входящих соединений с прочих узлов той же одноранговой сети.

  • --identity: Данный флаг является некоторой строкой, которая определяет собственное название частной сетевой среды.

Статические узлы

Если существует потребность соединения с определённым набором одноранговых узлов, тогда эти узлы могут быть добавлены в файл, в котором сохраняются chaindata и keystore, например, в ~/.ethereum/privatenet. Его названием должно быть static-nodes.json. Это может быть полезно для частной сетевой среды. В качестве примера такого файла json приведём такой:


[
"enode://
44352ede5b9e792e437c1c0431c1578ce3676a87e1f588434aff1299d30325c233c8d426fc5
7a25380481c8a36fb3be2787375e932fb4885885f6452f6efa77f@xxx.xxx.xxx.xxx:TCP_PORT"
]
 	   

Pltcm xxx является значением общедоступного IP адреса, а TCP_PORT может быть любым допустимым и доступным портом TCP в данной системе. Значение длинной шестнадцатеричной строки является идентификаторм данного узла.

Запуск частной сети

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


$ geth --datadir ~/.ethereum/privatenet init./privether/privategenesis.json
		

Она произведёт вывод, аналогичный приведённому на следующем снимке экрана:

 

Рисунок 8-2


Инициализация частной сетевой средой

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


$ geth --datadir .ethereum/privatenet/ --networkid 786
		

Она осуществит следующий вывод:

 

Рисунок 8-3


Запуск geth для частной сетевой средой

Теперь к geth можно подключаться через IPC для исполнения клиента geth в частной сети с помощью приводимой далее команды. Это позволит нам осуществлять взаимодействие посредством выполнения сеанса geth в данной частной сетевой среде:


$ geth attach ipc:.ethereum/privatenet/geth.ipc
		

Как показывается здесь, она откроет необходимую консоль взаимодействия JavaScript для выполнения сеанса Частной сети:

 

Рисунок 8-4


Запуск geth для подключения к Частной сети 786

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

[Замечание]Замечание

WARNING: No etherbase set and no accounts found as default.

(Предостережение: не обнаружено никакой основы эфира и никаких учётных записей, установленными по умолчанию.)

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

Следующая команда создаст некую новую учётную запись. В данном контексте такая учётная запись будет создана в Частной сетевой среде с Идентификатором 786:


> personal.newAccount("Password123") 
"0x76f11b383dbc3becf8c5d9309219878caae265c3"
		

После того как эта учётная запись создана, послежующим шагом будет установка её в качестве учётной записи Etherbase/ coinbase с тем, чтобы на эту учётную запись поступали вознаграждения за майнинг. Это можно осуществить такой командой:


> miner.setEtherbase(personal.listAccounts[0])
true
		

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


> eth.getBalance(eth.coinbase).toNumber();
0
		

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


> miner.start(2)
true
		

После запуска майнинга выполняется самая первая выработка DAG и осуществляется вывод, подобный следующему:

 

Рисунок 8-5


Выработка DAG

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

 

Рисунок 8-6


Вывод добычи

майнинг можно остановить следующей командой:


> miner.stop
true
		

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


> eth.getBalance(eth.coinbase).toNumber();
2.72484375e+21
		

Если в последовательности присутствуют два пробела и две табуляции, будет отображён полный перечень всех доступных объектов. Он отображается в приводимом далее снимке экрана:

 

Рисунок 8-7


Доступные объекты

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

 

Рисунок 8-8


Доступные методы

Дополнительно к упомянутым ранее командам, чтобы получить список доступных методов, после наборы любой команды воодится точка с запятой (;). На снимке экрана внизу приводится некий пример, который отображает список всех методов, доступных для net:

 

Рисунок 8-9


Список методов

Имеется несколько других команд, которые могут применяться для опроса имеющейся частной сети. Несколько примеров приводится далее:

  • Получить текущую стоимость газа:

    
    > eth.gasPrice
    20000000000
    		
  • Получить самый последний номер блока:

    
    > eth.blockNumber
    587
    		

В случае возникновения проблем неполадок может оказаться удобной отладка. Здесь приводится некий простой пример; однако доступно множество методов. Следующий метод возвратит значение RLP блока 0:

  • Дешифрация с помощью RLP:

    
    > debug.getBlockRlp(0)
    "f901f7f901f2a0000000000000000000000000000000000000000000000000
    0000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a
    7413f0a142fd40d49347940000000000000000000000000000000000000000
    a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363
    b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5
    e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc00162
    2fb5e363b421b9010000000000000000000000000000000000000000000000
    00000000000000000000000000000000000000000000000000000000000000
    00000000000000000000000000000000000000000000000000000000000000
    00000000000000000000000000000000000000000000000000000000000000
    00000000000000000000000000000000000000000000000000000000000000
    00000000000000000000000000000000000000000000000000000000000000
    00000000000000000000000000000000000000000000000000000000000000
    00000000000000000000000000000000000000000000000000000000000000
    000000000000000000000000000000000082020080834c4b40808000a00000
    00000000000000000000000000000000000000000000000000000000000088
    0000000000000042c0c0"
    		
  • Освобождение определённой учётной записи перед отправкой транзакций:

    
    > personal.unlockAccount
    ("0x76f11b383dbc3becf8c5d9309219878caae265c3")
    Unlock account 0x76f11b383dbc3becf8c5d9309219878caae265c3
    Passphrase:
    		
  • Отправка транзакций:

    
    > eth.sendTransaction({from:
    "0x76f11b383dbc3becf8c5d9309219878caae265c3", to:
    "0xcce6450413ac80f9ee8bd97ca02b92c065d77abc", value: 1000})
    		

    Другой способ состоит в применении метода listAccounts[], это может быть выполнено так, как отображено ниже:

    
    > eth.sendTransaction({from: personal.listAccounts[0], to:
    personal.listAccounts[1], value: 1000})
    		
  • Получить список компиляторов. Отметим, что если не будет отображено никакого вывода, это означает, что не установлено никакого компилятора solidity; подробности установки компилятора solidity предоставляются позднее в данной главе:

    
    > web3.eth.getCompilers()
    ["Solidity"]
    		

Исполнение Mist в частной сети

Имеется возможность исполнять Mist в Частной сети вызывая приводимые далее команды. Эти исполняемые файлы обычно доступны в имеющейся папке home после установки /opt/Ethereum:


$ ./Ethereum\ Wallet --rpc ~/.ethereum/privatenet/geth.ipc
		

Она сделает доступным некое соединение для исполнения сеанса geth Частной сети и предоставит все функции, такие как кошелёк (wallet), управление учётными записями, а также разработку контрактов в Частной сети через Mist.

 

Рисунок 8-10


Выполнение Кошелька Эфириум для соединения с Частной сетью

После того, как Эфириум запущен, будет отображён тот интерфейс, который показывается ниже, что ясно указывает на то он исполняется в режиме PRIVATE-NET.

 

Рисунок 8-11


Mist в Частной сети

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


--rpc http://127.0.0.1:8545
		

Оснащение контрактов с помощью Mist

С помощью Mist очень просто развернуть новый контракт. Mist предоставляет некий интерфейс, в котором могут писаться контракты в solidity и заетм развёртываться в имеющейся сетевой среде.

В данном примере будет применён некий простейший контракт, который может выполнять несложные арифметические вычисления над входными параметрами. Здесь показываются шаги того, как применять Mist для оснащения данного контракта. Хотя мы и не сделали ещё пока введения в solidity, наша цель здесь состоит в том, чтобы позволить пользователю поэкспериментировать с развёртыванием контракта и процессом взаимодействия. Дополнительная информация о кодировании и solidity будет предоставлена позднее в этой главе, после чего станет проще понимать показанный здесь код. те из вас, кто уже знаком с JavaScript или любым иным подобным языком программирования найдут этот код объясняющим себя самостоятельно.

Приведём сам пример исходного кода контракта:


pragma solidity ^0.4.0;
contract SimpleContract2
{
  uint x;
  uint z;
  function addition(uint x) returns (uint y)
  {
    z=x+5;
    y=z;
  }
  function difference(uint x) returns (uint y)  
  {
    z=x-5;
    y=z;
  }
  function division(uint x) returns (uint y)
  {
    z=x/5;
    y=z;
  }
  function currValue() constant returns (uint)
  {
    return z;
  } 
}
 	   

Данный код просто копируется в Mist в текущий раздел контракта, как показано здесь. С левой стороны может быть скопирован сам исходный код; после проверки и когда не будет обнаружено никаких синтаксических ошибок, появится необходимая опция для развёртывания данного контракта в имеющемся ниспадающем меню с правой стороны, в котором будет указано SELLECT CONTRACT TO DEPLOY. Просто выбирите имеющийся контракт и нажмите на Deploy в его кнопке данного экрана.

 

Рисунок 8-12


Разработка контракта в браузере Mist

запросит пароль данной учётной записи и отобразит некое окно, аналогичное показанному ниже снимку экрана:

 

Рисунок 8-13


Создание контракта с помощью Mist

введите требующийся пароль и кликните по SEND TRANSACTION для развёртывания данного контракта.

После успешного развёртывания и майнинга, он появится в полном перечне транзакций в mist, как показано тут:

 

Рисунок 8-14


Перечень транзакций после создания контракта в Mist

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

 

Рисунок 8-15


Взаимодействие с имеющимся контрактом в Mist

В нашем предыдущем снимке экрана доступны опции READ FROM CONTRACT и WRITE TO CONTRACT. Кроме того, с правой стороны можно видеть те функции, которые выставляются самим контрактом. После выбора требуемой функции, вводится соответствующее значение для данной функции и выбирается значение учётной записи (от имени которой необходимо исполнение); чтобы исполнить данную транзакцию нажмите execute, что в результате вызовет выбранную функцию данного контракта.

Весь процесс отображён на снимке экрана:

 

Рисунок 8-16


Исполнение контракта в Mist

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

Инструменты и клиенты разработки

Имеется целый ряд инструментов доступных для разработки Эфириум. Следующая схема показывает соподчинение различных инструментов, клиентов, IDE и инфраструктур разработки для Эфириум:

 

Рисунок 8-17


Упорядочение компонентов экосистемы Эфириум

В данной главе мы в основном сосредоточимся на geth, браузере solidity, solc и truffle. Прочие элементы будут рассмотрены кратко.

Языки

Контракты можно программировать на различных языках. Имеются четыре языка для написания контракта:

  • Mutan: Это язык в стиле Go, который был объявлен устаревшим в начале 2015 и с тех пор не применяется.

  • LLL: Данный язык является подобным Лисп. Он также более не применяется.

  • Serpent: Это простой и ясный язык, подобный Python. Он активно применяется для разработки контрактов.

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

Компиляторы

Компиляторы применяются для преобразования исходного кода высокого уровня в тот формат, который понимает среда выполнения Эфириум. Рассматриваемый компилятор solidity является наиболее распространённым в применении и именно он обсуждается здесь.

Solc

Компилятор solidity выполняет преобразование с языка высокого уровня solidity в байтовый код EVM (Ethereum Virtual Machine) с тем, чтобы его можно было исполнять в текущем блокчейне в EVM.

Компилятор solidity в операцилнной системе Linux Ubuntu может быть установлен с применением следующих команд:


$ sudo apt-get install solc
		

Если PPA ещё не установлен, его можно установить следующим образом:


sudo add-apt-repository ppa:ethereum/ethereum
sudo apt-get update
		

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


$ solc --version
solc, the solidity compiler commandline interface
Version: 0.4.6+commit.2dabbdf0.Linux.g++
		

Solc поддерживает различные функции. Ниже приводятся некоторые примеры:

  • Отобразить контракт в двоичном виде.

     

    Рисунок 8-18


    Вывод исполняемого файла solidity

  • Оценка газа:

    
    imran@drequinox-OP7010:~$ solc --gas contract1.sol
    ======= SimpleContract =======
    Gas estimation:
    construction:
      97 + 54600 = 54697
    external:
      division(uint256): 230
      addition(uint256): 231
      difference(uint256): 253
    internal:
    		

Заметим, что contrat1.sol показан исключительно в качестве примера; этот файл может содержать любой код solidity адаптивного контракта (smart contract). Сам код данного файла не отображён здесь.

Solc применяется внутри web3 из имеющейся консоли geth чтобы скомпилировать полученный контракт. Здесь отображается синтаксис, и в нём contractsourcecode является исходным кодом solidity:


web3.eth.compile.solidity(contractsourcecode)
		

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

IDE (Интегрированные среды разработки)

Для разработки solidity существуют различные доступные Интегрированные среды разработки (IDE). Большинство IDE доступно через интернет и представляется в интерфейсах веб. Для контрактов меньшего размера наиболее часто применяется браузер solidity, который и обсуждается здесь.

Браузер Solidity

Браузер Solidity является средой на веб основе для проведения разработки и тестирования контрактов с применением solidity. Он не работает в блокчейне реального времени; на самом деле это среда эмуляции, в которой контракты могут развёртываться, тестироваться и отлаживаться. Он доступен по ссылке https://ethereum.github.io/browser-solidity. Ниже приводится пример интерфейса:

 

Рисунок 8-19


Браузер solidity

С левой стороны имеется редактор кода с подсветкой синтаксиса и форматированием текста, а с правой стороны представлен ряд инструментов, для их применения в развёртывании, отладке, тестировании и взаимодействии с определённым контрактом. Доступны различные функции, такие как взаимодействие с транзакцией, вариант подключения к ВМ JavaScript, настройка среды исполнения, отладчик, проверка формальностей и статичный анализ. Они могут настраиваться для соединения со средой исполнения, такой как ВИ JavaScript, внедрённым Web3 - в которой Mist или некая аналогичная среда предоставляет окружение для работы - или поставщика услуг Web3, который делает возможным соединение с локально исполняемым клиентом Эфириум (например, geth) через IPC или RPC поверх HTTP (предоставляемый поставщиком терминал web3).

Ремикс

Псоле прекращения в августе 2016 Mix IDE, был запущен проект Remix. Remix является IDE на основе браузера, который в настоящее время интенсивно разрабатывается, причём на текущий момент присутствует только часть, касающаяся отладчика. Этот отладчик очень мощный и может применяться для выполнения подробного уровня отслеживания и анализа байтового кода EVM. В нашем следующих разделах будут представлены установка и примеры использования Remix.

Установка

Remix доступен на https://github.com/ethereum/remix. Самым первым шагом является клонирование имеющегося репозитория GitHub:


$ git clone https://github.com/ethereum/remix
Cloning into 'remix'...
remote: Counting objects: 2185, done.
remote: Compressing objects: 100% (213/213), done.
remote: Total 2185 (delta 124), reused 0 (delta 0), pack-reused 1971
Receiving objects: 100% (2185/2185), 1.12 MiB | 443.00 KiB/s, done.
Resolving deltas: 100% (1438/1438), done.
Checking connectivity... done.
		

После успешного выполнения предыдущих шагов, исполните такие команды:


cd remix
npm install
npm run build
		

Сейчас можно либо исполнить npm run start_node, либо может быть запущен geth с использованием надлежащих флагов. После того как geth поднят и выполняется, можно поработать с простым веб сервером для обслуживания веб страницы remix.

Теперь geth может быть запущен с помощью такой команды:


$ geth --datadir .ethereum/privatenet/ --networkid 786 --rpc --rpcapi 'web3,eth,debug' --rpcport 8001 --rpccorsdomain 'http://localhost:7777'
		

Отметим флаг --rpcapi; он необходим чтобы сделать возможными web3, eth, debug поверх RPC.

Если npm run start_node не работает, может появится такое сообщение:


$ npm run start_node
> ethereum-remix@0.0.2-alpha.0.0.9 start_node /home/imran/remix
> ./runNode.sh
both eth and geth has been found in your system
restart the command with the desired client:
npm run start_eth
or
npm run start_geth
		

В предположении, что требуется geth, воспользуемся такой командой:


$ npm run start_geth
		

Если предполагалась необходимость исполнения geth, тогда потребуется некий простейший веб сервер для просмотра веь страницы remix. Это можно сделать вызвав такую команду Python, которая показана ниже. Её необходимо исполнить в каталоге remix.

 

Рисунок 8-20


Простейший веб сервер Python

Если эта команда успешна и веб сервер исполняется, remix можно просматривать с применением URL http://localhost:7777, что показано на снимке экрана внизу:

 

Рисунок 8-21


Веб браузер показывает исполняемый remix, обслуживаемый через порт TCP 7777

Remix также доступен в качестве части браузера solidity (браузер solidity будет обсуждён отдельно позднее). К нему можно подключиться через локальную Частную сетевую среду, выдающую терминал web3 поставщика. Это можно продемонстрировать так:

 

Рисунок 8-22



Опция Web3 Provider в браузере solidity для локального узла geth выглядит следующим образом:

 

Рисунок 8-23


Отладчик Remix Debugger в браузере solidity

Инструменты и библиотеки

В Эфириум доступны различные инструменты и библиотеки. Наиболее употребимые обсуждаются здесь.

Node.js версии 7

Так как Node.js требуется для большей части применяемых инструментов и библиотек, он может быть установлен при помощи такой команды:


curl -sL https://deb.nodesource.com/setup_7.x | sudo -E bash -sudo apt-get install -y nodejs
		

Локальный проводник блока Эфириума

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

В машине Linux Ubuntu исполните следующую команду чтобы установить необходимый проводник блока Эфириум:


$ git clone https://github.com/etherparty/explorer
		

Она отобразит вывод, аналогичный такому:


Cloning into 'explorer'...
remote: Counting objects: 253, done.
remote: Total 253 (delta 0), reused 0 (delta 0), pack-reused 253
Receiving objects: 100% (253/253), 51.20 KiB | 0 bytes/s, done.
Resolving deltas: 100% (130/130), done.
Checking connectivity... done.
		

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


imran@drequinox-OP7010:~$ cd explorer/
imran@drequinox-OP7010:~/explorer$ npm start
> EthereumExplorer@0.1.0 prestart /home/imran/explorer
> npm install
		

По окончанию установки будет отображён вывод, аналогичный приводимому ниже, в котором запускается сервер HTTP для проводника Эфириум:

 

Рисунок 8-24


Сервер HTTP для проводника Эфириум

Когда веб сервер поднят, geth следует запустить такой командой:


geth --datadir .ethereum/privatenet/ --networkid 786 --rpc --rpccorsdomain 'http://localhost:8000'
		

После успешного запуска geth, в локальном хосте переместитесь на TCP port 8000, как это показано здесь, чтобы получить доступ к имеющемуся локальному проводнику блока Эфириум.

 

Рисунок 8-25


Проводник блока Эфириум

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


imran@drequinox-OP7010:~/explorer/app$ python -m SimpleHTTPServer 9900
Serving HTTP on 0.0.0.0 port 9900 ...
		

Наш клиент geth необходимо запустить с соответствующими параметрами. Если это будет не так, может отобразиться ошибка подобная показанной на снимке экрана:

 

Рисунок 8-26


Ошибочное сообщение проводника блока Эфириум

Перезапустите geth чтобы сделать доступным rpccorsdomain:


geth --datadir .ethereum/PrivateNet/ --networkid 786 --rpc --rpccorsdomain 'http://192.168.0.17:9900'
		

EthereumJS

Временами проверку невозможно выполнять в Тестовой сети и, очевидно, Основная сетевая среда не является местом для проведения проверок. Частная сетевая среда может требовать значительного времени для настройки в данном случае. Когда требуется быстрая проверка и нет подходящей тестовой среды, на помощь приходит EthereumJS testrpc. Она применяет EthereumJS для имитации необходимого поведения клиента geth Эфириум и делает возможным быструю проверку оснащения. Testrpc доступна через npm в качестве пакета узла.

Прежде чем установить testrpc, необходимо также иметь уже установленным Node.js, а также должен быть доступным требуемый пакет npm.

Testrpc можно установить при помощи такой команды:


npm install -g ethereumjs-testrpc
		

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


$testrpc
		

Разработка и развёртывание контракта

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

Этап написания обсуждается совместно с написанием исходного кода в solidity. Его можно выполнять в любом текстовом редакторе. Существуют различные встраиваемые модули, доступные для Vim в Linux, Atom и прочих редакторах, которые предоставляют подсвечивание синтаксиса и форматирование для исходного кода solidity.

Тестирование обычно выполняется автоматическими средствами. Позднее в данной главе мы познакомим вас с truffle, который применяет инфраструктуру Mocha для тестирования контрактов. Однако, также можно выполнять тестирование и вручную. После того как ваш контракт проверен, работает и оттестирован в среде эмуляции (например, в EthereumJS testrpc) или в Частной сетевой среде, он может быть развёрнут при помощи Тестовой сетевой среды Ropsten и окончательно в действующий блокчейн (Homestead).

В нашем следующем разделе вы познакомитесь с языком программирования solidity. Это некое краткое введение в solidity, которое должно предоставить основные знания, необходимые для написания требующегося вам контракта. Его синтаксис очень похож на C и JavaScript и он очень прост для программирования.

Введение в Solidity

Solidity является подходящим для данной области язык программирования контрактов в Эфириум. Тем не менее, имеются и прочие языки, такие как Mutan и LLL, однако solidity является наиболее популярным на момент написания данных строк. Его синтаксис близок к JavaScript и C. Solidity на протяжении последних нескольких лет превратился в зрелый язык и его достаточно просто применять, но ему ещё предстоит долгий путь, прежде чем он станет отлаженным и насыщенным функциональностью как и прочие хорошо зарекомендовавшие себя языки. Тем не менее, именно он в настоящее время является наиболее распространённым языком программирования контрактов.

Он относится к языкам со статическими типами, что означает что проверка типа переменной в solidity осуществляется на момент компиляции. Всякая переменая, будь она статичной или локальной, должна быть определена с неким типом на момент компиляции. Это полезно в смысле того, что все валидации и проверки выполняются на этапе компиляции и определённые типы ошибок, такие как интерпретация типов данных могут быть отловлены на ранней стадии цикла разработки, вместо того чтобы быть выявленными в момент исполнения, что может быть дорогостоящим, в особенности в случае подхода имеющегося блокчейна/ адаптивных контрактов (smart contracts). Прочие свойства данного языка включают в себя наследование, библиотеки и возможность определения составных типов данных.

Solidity также именуется ориентированным под контракты языком. В solidity контракты эквивалентны понятию классов в прочих объектно- ориентированных языках программирования.

Типы

Solidity имеет два вида типов данных: определяемых значением типы и ссылочные типы.

Определяемые значением типы

Здесь объясняются подробности определяемых значением типы.

Булевы

Этот тип данных имеет два возможных значения, истина (true) или ложь


       bool v = true;
 	   
(false), например:

Этот оператор присваивает значение true переменной v.

Целые числа

Данный тип данных представляет целые. Здесь показана таблица, которая отображает различные ключевые слова, применяемые для объявления целых типов данных.

Таблица 8-1. Целые типы данных
Ключевое слово Типы Подробности

int

Целое со знаком

С int8 по int256, что подразумевает что ключевые слова доступны вплоть до int256 с приращением 8, например, int8, int16, int24.

Целое без знака

С uint8 по uint256.

Например, в данном коде, отметим, что uint является сокращением для uint256:


       uint256 x;
       uint y;
       int256 z;
 	   

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


       uint constant z=10+10;
 	   

Статические переменные определяются вне пределов тела некоторой функции и они остаются доступными на протяжении всего контракта в зависимости определённой для них доступности пока существует этот контракт.

Адреса

Этот тип данных хранит значение длиной 160- бит (20 байт). Этот тип имеет различные свойства, которые могут применяться для взаимодействия и в качестве запроса в контрактах. Здесь объясняются эти свойства:

Баланс

Такое свойство, баланс, возвращает значение баланса данного адреса в Wei.

Отправка

Это свойство применяется для отправки некоторого количества эфира по какому- то адресу (160- битный адрес Эвириума) и возвращает true или false в зависимости от полученного результата такой транзакции, например:


       address to = 0x6414cc08d148dce9ebf5a2d0b7c220ed2d3203da;
       address from = this;
       if (to.balance < 10 && from.balance > 50) to.send(20);
 	   

Функция вызова

Значение call, callcode и delegatecall предоставляются для взаимодействия с функциями, у которых нет ABI (Application Binary Interface, Двоичного интерфейса приложения). Эти функции следует применять с осторожностью, так как они не безопасны для применения из- за влияния на тип сохранности и безопасности имеющихся контрактов.

Типы значения массива (байтовые массивы фиксированного и динамического размера)

Solidity имеет массивы байт с фиксированным размером и динамические массивы байт. Ключевые слова фиксированного размера находятся в диапазоне от bytes1 до bytes32, в то время как ключевые слова динамических размеров содержат байты и строки. bytes применяются для сырых байтов данных, а строки применяются для строк в кодировке UTF-8. Так как эти массивы возвращаются по своему значению, их вызов потребует стоимости газа. length является элементом типов значения массива и возвращает величину длины данного массива байт.

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


       bytes32[10] bankAccounts;
 	   

Некий пример массива с динамическим размером выглядит так:


       bytes32[] trades;
 	   

Получить длину trades:


       trades.length;
 	   

Литералы

Применяются для представления некоторого фиксированного значения.

Целочисленные литералы

Целочисленные литералы являются последовательностью десятичных цифр из диапазона 0-9. В качестве примера приведём:


       uint8 x = 2;
 	   

Строковые литералы

Строковые литералы определяют некий набор символов, записанных внутри одинарных или двойных кавычек. Приведём пример:


       'packt'
       "packt"
 	   

Шестнадцатеричные литералы

Шестнадцатеричные литералы имеют префикс hex и определяются внутри двойных или одинарных кавычек. Пример:


       (hex'AABBCC');
 	   

Перечисления

Делает возможным создание определяемых пользователем типов. Вот некоторые примеры:


       enum Order{Filled, Placed, Expired };
       Order private ord;
       ord=Order.Filled;
 	   

Для перечислений разрешены преобразования в явном виде в- или из- целых типов.

Типы функций

Существует два типа функций: внутренние и внешние.

Внутренние функции

Могут применяться только в рамках определённого в текущем контракте контекста.

Внешние функции

Внешние функции могут вызываться через вызовы внешних функций.

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

Ниже демонстрируется синтаксис объявления функции:


function <nameofthefunction> (<parameter types> <name of the variable>) {internal|external} [constant] [payable] [returns (<return types> <name of the variable>)]
 	   

Ссылочные типы

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

Массивы

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


       uint[] OrderIds;
 	   

Структуры

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


       Struct Trade
       {
       uint tradeid;
       uint quantity;
       uint price;
       string trader;
       }
 	   

Местоположение данных

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

Соответствия

Соответствия (mapping) применяются в качестве ключевого слова для соотнесения со значением. Это некий способ связать какое- то значение с ключом. Все значения в этом соответствии уже инициализированы полностью нулевым значением , например:


       mapping (address => uint) offers;
 	   

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


       mapping (string => uint) bids;
       bids["packt"] = 10;
 	   

Это в основном словарь или таблица хэшей, в котором строковые значения соответствуют неким целым значениям. Данное соответствие с названием bids имеет строковое значение packt, соответствующее значению 10.

Глобальные переменные

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

Некое подмножество доступных функций и переменных выглядит следующим образом:


       keccak256(...) returns (bytes32)
 	   

Данная функция применяется для вычисления хэша keccak256 аргументов, предоставляемых в такую функцию:


       ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)
 	   

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


       block.number
 	   

Возвращает номер текущего блока.

Структуры управления

Доступными в solidity управляющими структурами являются if - else, do, while, for, break, continue и return. Они работают подобно тому, как это происходит в языке C или JavaScript.

События

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

В нашем простом примере здесь событие valueEvent возвратит true если значение параметра x, переданного в function Matcher больше или равно чем 10:


contract valueChecker {
    uint8 price=10;
    event valueEvent(bool returnValue);
    function Matcher(uint8 x) returns (bool)
    {
        if (x>=price)
        {
            valueEvent(true);
            return true;
        }
    }
}
 	   

Наследование

В solidity поддерживается наследование. Для извлечения некоторого контракта из иного контракта применяется ключевое слово is. В следующем примере valuechecker2 извлекается из имеющегося контракта valuechecker. В извлечённом контракте можно осуществлять доступ ко всем не являющимися частными (private) свойствам (member) данного родительского контракта:


contract valueChecker
{
  uint8 price=10;
  event valueEvent(bool returnValue);
  function Matcher(uint8 x) returns (bool)
  {
    if (x>=price)
    {
      valueEvent(true);
      return true;
    }
  }
}
contract valueChecker2 is valueChecker
{
  function Matcher2() returns (uint)
  {
    return price + 10;
  }
}
 	   

В нашем предыдущем примере, если uint8 price = 10 изменить на uint8 private price = 10, тогда эта переменная не будет доступна в контракте valuechecker2. Это происходит по той причине, что теперь данное свойство объявляется как частное и к нему не разрешён доступ из какого- либо иного контракта.

Библиотеки

Библиотеки развёртываются только при вызове определённого адреса и его кода посредством кода операции CALLCODE/ DELEGATECALL в конкретной EVM. Ключевой идеей, стоящей за библиотеками является повторное применение кода. Они аналогичны контрактам и действуют как основные контракты в вызовах контрактов. Библиотеку можно определить так, как это показывается в следующем примере:


library Addition
{
  function Add(uint x,uint y) returns (uint z)
  {
    return x + y;
  }
}
 	   

Эту библиотеку затем можно вызвать в определённом контракте, как это показано ниже. Вначале её требуется импортировать, а затем её можно применять где- то в этом коде. Вот простой пример:


Import "Addition.sol"
function Addtwovalues() returns(uint)
{
  return Addition.Add(100,100);
}
 	   

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

Функции

Функции в solidity являются модулями кода, который связывается с неким контрактом. Функции объявляются их названием, необязательными параметрами, модификатором доступа, необязательным ключевым словом constant и опциональным типом возврата. Это демонстрируется следующим примером:


function orderMatcher(uint x) private constant returns(bool returnvalue)
 	   

В нашем предыдущем примере function является ключевым словом, применяемым для определения данной функции. orderMatcher является названием этой функции, uint x это необязательный параметр, private определяет модифиактор/ определитель доступа, который управляет доступом к этой функции из внешних контрактов, constant это необязательное ключевое слово, применяемое для описания того, что данная функция не изменяет ничего в этом контракте, а применяется вместо этого только для извлечения данных из определённого контракта, а returns (bool returnvalue) является необязательным типом возврата данной функции.

  • Как определить функцию: Синтаксис определения функции выглядит следующим образом:

    
    function <название функции>(<параметры>) <определеитель видимости> returns (<тип возвращаемых данных> <название переменной>)
    {
      <тело функции>
    }
     	   
  • Подпись функции: Функция в solidity идентифицируется по её подписи, которая является самыми первыми четырьмя байтами хэша кечак-256 её полной строки подписи. Она также отображается в браузере solidity, как это демонстрируется в снимке экрана ниже. D99c89cb являются первыми четырьмя байтами 32- байтного хэша кечак- 256 в функции с названием Matcher.

     

    Рисунок 8-27


    Хэш функции, отображаемый в браузере solidity

    В этом примере функция Matcher имеет соответствующую хэш- подпись d99c89cbю Данная информация полезна при построении интерфейса.

  • Входные параметры функции: Параметры входа функции определяются в виде <тип данных> <название параметра>. Наш пример поясняет это понятие, где uint x и uint y являются входными параметрами функции checkValues:

    
    contract myContract
    {
      function checkValues(uint x, uint y)
      {
      }
    }
     	   
  • Выводимые параметры функции: Параметры вывода функции определяются в виде <тип данных> <название параметра>. Данный пример отображает простейшую функцию, возвращающую некое значение uint:

    
    contract myContract
    {
      Function getValue() returns (uint z)
      {
        z=x+y;
      }
    }
     	   

    Функция может возвращать множество значений. В нашем предыдущем примере функции getValue возвращается только одно значение, однако некая функция может возвращать до 14 значений различных типов данных. Названия не используемых параметров возврата могут опционально опускаться.

  • Внутренние вызовы функции: Функции внутри контекста текущего контракта могут вызываться внутренне прямым образом. Такие вызовы выполняются для вызова тех функций, которые имеются внутри того же самого контракта. Данные вызовы имеют результатом простые вызовы JUMP на уровне кода байт EVM.

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

  • Функции перехода на нижний уровень: Это (Fall back) является какой- то функцией без названия в некотором контракте без аргументов и возвращаемых данных. Данная функция выполняется всякий раз при приёме эфира. Она требуется для реализации внутри некоторого контракта если контракт собирается получать эфир; в противном случае будет выставлена некая исключительная ситуация и эфир будет возвращён. Данная функция также исполняется если никакие иные подписи функции не находят соответствия в данном контракте. Если ожидается, что данный контракт получает эфир, тогда данная функция перехода на нижний уровень должна быть определена с модификатором payable. Этот payable необходим; в противном случае данная функция не будет способна принять какой- бы то ни было эфир. Данную функцию можно вызвать с помощью метода address.call() как, например, в такой демонстрации:

    
    function ()
    {
      throw;
    }
     	   

    В этом случае, если функция fallback вызывается в соответствии с описанными ранее условиями, она вызовет throw, что откатит назад в то состояние, которе было перед выполнением данного вызова. Это также может быть некая иная конструкция помимо throw; напрмиер, это может быть регистрация какого- то события, которое может использоваться для некоторого предупреждения для возврата полученного результата данного вызова обратившемуся к нему приложению.

  • Функции модификации: Эти функции применяются для изменения поведения какой- то функции и могут вызываться перед прочими функциями. Обычно они применяются для проверки некоторых условий перед исполнением основной функции. В такой модифицирующей функции применяется _ (символ подчерка), который будет замещён реальным телом самой функции при вызове данного модификатора. В основном, это символизирует то, что данная функция нуждается в ограждении. Данная концепция аналогична функциям защиты (guard) в прочих языках.

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

  • Описатели видимости функции (модификаторы доступа): Функции могут быть определены с четырьмя следующими описателями доступа:

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

    • Public: По умолчанию функции являются общедоступными. Они могут вызываться либо как внутренние, либо с применением сообщений.

    • Internal: Внутренние функции видны прочим производным контрактам из их родительского контракта.

    • Private: Частные функции видны только в том же самом контракте, в котором они определены.

  • Другие важные ключевые слова/ функции throw: throw применяется для останова исполнения. Как результат, Возвращаются обратно все изменения состояний. В данном случае никакой газ не возвращается инициатору данной транзакции, так как весь оставшийся газ потреблён.

Схема файла исходного кода Solidity

Прагма версии

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


pragma solidity ^0.5.0
 	   

Это обеспечит то, что данный исходный файл не будет компилироваться с версиями компилятора ниже чем 0.5.0, а также версиями, начинающимися с 0.6.0.

Импорт

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


Import "module-name";
 	   

Комментарии

В файл исходного кода solidity можно добавлять комментарии аналогично тому, как это делается в языке C. Комментарии на нескольких строках заключаются в /* и */, в то время как комментарии в одной строке стартуют с //.

Некий следующий пример программы solidity отображает применение прагмы, импорта и комментариев:

 

Рисунок 8-28


Пример программы solidity, отображаемый в браузере solidity

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

Введение Web3

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

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


$ geth --datadir .ethereum/privatenet/ --networkid 786 --rpc --rpcapi 'web3,eth,debug' --rpcport 8001 --rpccorsdomain 'http://localhost:7777'
		

Отметим флаг --rpcapi, который делает доступными методы web3, eth и debug.

Это мощная библиотека и её можно и дальше изучать подключив некий экземпляр geth. Далее в этом разделе вы ознакомитесь с основными понятиями и техниками, которые применяются в web3 через интерфейсы JavaScript/HTML.

Экземпляр geth может быть подключён с применением следующей команды:


$ geth attach ipc:.ethereum/privatenet/geth.ipc
		

После того, как запущена консоль geth, может быть запрошен web4, например:

 

Рисунок 8-29


web3 через geth

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


pragma solidity ^0.4.0;
contract valueChecker {
    uint price=10;
    event valueEvent(bool returnValue);
    function Matcher (uint8 x) returns (bool)
    {
        if (x>=price)
        {
            valueEvent(true);
            return true;
        }
    }
}
		

Теперь откроем консоль geth, которую мы уже открывали ранее и осуществим следующие шаги:

  1. Определим некую переменную с именем simplecontractsource и назначим ей код своей программы:

    
    > var simplecontractsource = "pragma solidity ^0.4.0; contract valueChecker { uint price=10;event valueEvent(bool returnValue); function Matcher (uint8 x) returns (bool) { if (x>=price) {valueEvent(true); return true; } } }"
     	   

    Он отобразит следующий вывод:

    
    undefined
     	   

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

    
    $ tr --delete '\n' < valuechecker.sol > valuecheckersingleline.sol
     	   

    В нашем предыдущем примере valuechecker.sol является файлом, который содержит несколько символов новой строки, \n, а valuecheckersingleline.sol является файлом вывода, произведённым после удаления всех сивмволов новой строки из файла на входе. Данный код затем может быть скопирован и помещён из файла в нашу консоль JavaScript geth.

  2. Теперь убедимся что необходимый нам компилятор solidity доступен и, если он не доступен, тогда отсылаем вас к тому разделу этой главы, в которой объяснялась установка solidity.

    
    > eth.getCompilers()
    ["Solidity"]
     	   
  3. Создайте переменную, назначьте ей код и откомпилируйте его с применением solidity:

    
    > var simplecontractcompiled=eth.compile.solidity(simplecontractsource)
    undefined
     	   
  4. Введите simplecontractcompiled; это отобразит вывод аналогичный показываемому ниже, так как simplecontractcompiled были назначены данные с предыдущего шага 3.

     

    Рисунок 8-30


    Вывод simplecontractcompiled

  5. Создайте переменную для взаимодействия с имеющимся контрактом:

    
    > var simplecontractinteractor=eth.contract 
    (simplecontractcompiled.valueChecker.info.abiDefinition);
    undefined
     	   
  6. Проверьте ABI (Application Binary Interface, Двоичный интерфейс ABI):

    
    > simplecontractinteractor.abi
    [{
        constant: false,
        inputs: [{
            name: "x",
            type: "uint8"
        }],
        name: "Matcher",
        outputs: [{
            name: "",
            type: "bool"
        }],
        payable: false,
        type: "function"
    }, {
        anonymous: false,
        inputs: [{
            indexed: false,
            name: "returnValue",
            type: "bool"
        }],
        name: "valueEvent",
        type: "event"
    }]
     	   
  7. Убедитесь что код valueChecker представлен в шестнадцатеричном виде:

    
    > simplecontractcompiled.valueChecker.code
     	   

    Это отобразит следующий вывод. У вас он может слегка отличаться:

    
    "0x6060604052600a60005534610000575b60878061001c6000396000f36060604
    05260e060020a6000350463f9d55e218114601c575b6000565b3460005760296004
    35603d565b604080519115158252519081900360200190f35b6000805460ff83161
    0608157604080516001815290517f3eb1a229ff7995457774a4bd31ef7b13b6f449
    1ad1ebb8961af120b8b4b6239c9181900360200190a15060015b5b91905056"
     	   
  8. Теперь введите следующую порцию кода; отметим, что само поле данных содержит приведённый выше код для simplecontractcompiled:

    
    > var simplecontractTransaction = simplecontractinteractor.new({
        from: eth.coinbase,
        data: simplecontractcompiled.valueChecker.code,
        gas: 2000000
    },
    function(err, contract) {
        if (err) {
            console.error(err);
        } else {
            console.log(contract);
            console.log(contract.address);
        }
    });
     	   

    Допустим, что он вернул некое сообщение об ошибке:

    
    Error: account is locked
    Undefined
     	   

    Если это так, разблокируйте необходимую учётную запись при помощи показанной далее команды.

    Вначале выведем список всех учётных записей, воспользовавшись следующей командой для получения идентификаторов имеющихся учётных записей:

    
    > personal.listAccounts
    ["0x76f11b383dbc3becf8c5d9309219878caae265c3",
    "0xcce6450413ac80f9ee8bd97ca02b92c065d77abc"]
     	   

    Введите следующую команду с соответствующей учётной записью для её разблокирования:

    
    > personal.unlockAccount
    ("0x76f11b383dbc3becf8c5d9309219878caae265c3")
    Unlock account 0x76f11b383dbc3becf8c5d9309219878caae265c3
     	   

    Введите пароль этой учётной записи:

    
    Passphrase:
    true
     	   

    После разблокирования учётной записи введите предыдущий код снова; допустим возникла некая ошибка:

    
    > Error: The contract code couldn't be stored, please check your gas amount.
     	   

    На этот раз попробуйте увеличить объём газа. Если введено слишком блоьшое количество газа, тогда появится такая ошибка:

    
    > Error: Exceeds block gas limit
    undefined
     	   
  9. После того, как требуемая учётная запись успешно разблокирована, запустите майнинг с тем, чтобы данный контракт мог подвергаться добыче (для запуска майнинга нет необходимости в разблокировании. Разблокирование учётной записи необходимо для добычи данного контракта и его создания в имеющемся блокчейне):

    
    > miner.start()
    true
     	   

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

    
    > [object Object]
    undefined
    undefined
    > [object Object]
    0x94a1107f2585f0ab931c71f2f8f02e9f5ab888c0
     	   

    Он демонстрирует сам адрес только что созданного контракта после добычи этого контракта.

  10. Чтобы сделать взаимодействие с этим контрактом проще, значение адреса его учётной записи может быть присвоено некоторой переменной:

    
    > var simplecontractaddress="0x94a1107f2585f0ab931c71f2f8f02e9f5ab888c0"
    Undefined
     	   
  11. Теперь имеется множество выставленных методов и этот контракт можно теперь опрашивать далее, например:

    
    > var deployedaddress=eth.getCode(simplecontractaddress);
    undefined
    > deployedaddress
    "0x606060405260e060020a6000350463f9d55e218114601c575b6000565b346000
    576029600435603d565b604080519115158252519081900360200190f35b6000805
    460ff831610608157604080516001815290517f3eb1a229ff7995457774a4bd31ef
    7b13b6f4491ad1ebb8961af120b8b4b6239c9181900360200190a15060015b5b919
    05056"
    > eth.getBalance(simplecontractaddress)
    0
     	   
  12. После этого можно создать некий объект с названием simplecontractinstance, который будет применяться для вызова методов:

    
    simplecontractinstance = web3.eth.contract(simplecontractcompiled.valueChecker.info.abiDefinition).at(simplecontractaddress);
     	   
  13. Имеется множество методов, которые теперь можно выставлять и их перечень может выглядеть следующим образом:

    
    > simplecontractinstance.Matcher.
    simplecontractinstance.Matcher.apply
    simplecontractinstance.Matcher.constructor
    simplecontractinstance.Matcher.request
    
    simplecontractinstance.Matcher.arguments
    simplecontractinstance.Matcher.estimateGas
    simplecontractinstance.Matcher.sendTransaction
    
    simplecontractinstance.Matcher.bind
    simplecontractinstance.Matcher.getData
    simplecontractinstance.Matcher.toString
    
    simplecontractinstance.Matcher.call
    simplecontractinstance.Matcher.length
    simplecontractinstance.Matcher.uint8
    
    simplecontractinstance.Matcher.caller
    simplecontractinstance.Matcher.prototype
     	   
  14. Этот контракт можно опрашивать и далее, как это демонстрируется тут. В нашем следующем примере вызывается функция Matcher с заданными аргументами. Помните, что в её коде имеется некое условие, которое выполняет проверку того, что если данное значение равно или превышает 10, тогда функция возвращает true; в противном случае она возвращает false. Это может выглядеть следующим образом:

    
    > simplecontractinstance.Matcher.call(12)
    true
    > simplecontractinstance.Matcher.call(9)
    false
    > simplecontractinstance.Matcher.call(0)
    false
    > simplecontractinstance.Matcher.call(12)
    true
     	   

Запросы POST

Можно взаимодействовать с geth через jsonrpc поверх HTTP. С этой целью можно применять curl. Чтобы ознакомить вас с запросами POST и показать как делать запросы POST с помощью curl, здесь показаны некоторые примеры. Curl доступен на https://curl.haxx.se/.

Прежде чем применять интерфейс JsonRPC поверх HTTP, необходимо запустить geth с соответствующими ключами, которые показаны здесь:


--rpcapi web3
 	   

Этот переключатель разрешает интерфейс web3 поверх HTTP.

Для целей взаимодействия поверх HTTP может применяться команда Linux curl, как это демонстрируется рядом примеров.

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

    
    $ curl --request POST --data
    '{"jsonrpc":"2.0","method":"personal_listAccounts","params":[],"id":4}' localhost:8001
    		

    Она возвращает определённый вывод, некий объект JSON с полным списком учётных записей:

    
    {"jsonrpc":"2.0","id":4,"result":
    ["0x76f11b383dbc3becf8c5d9309219878caae265c3","0xcce6450413ac80f9ee8bd97ca02b92c065d77abc"]}
    		

В предыдущей команде curl применялся переключатель --request для определения необходимой команды, POST в качестве типа запроса и --data применялся для определения необходимых параметров и значений, а также, в конце концов, localhost:8001 является тем местом, в котором открыт терминал HTTP из geth.

Интерфейс HTML и JavaScript

Желательно взаимодействовать с контрактами в дружественной пользователю манере через какую- то веб страницу. Имеется возможность взаимодействовать с такими контрактами при помощи библиотеки web3.js с веб страниц на основе HTML/JS/CSS. Такое содержимое HTML можно обслуживать с применением любого веб сервера HTTP, в то время как web3.js может подключаться через локальный RPC с исполняемым клиентом Эфириум (geth) и предоставлять некий интерфейс для наших контрактов в имеющемся блокчейне. Такую архитектуру можно отобразить на следующей схеме:

 

Рисунок 8-31


Архитектура взаимодействия web3.js, интерфейса и блокчейна

Если web3.js ещё не установлен, воспользуйтесь приводимыми шагами; в противном случае переместитесь к следующему разделу.

Установка web3.js

Web3 можно установить с помощью npm просто вызвав следующую команду:


$ npm install web3
		

Он также может быть выгружен напрямую с https://github.com/ethereum/web3.js.

web3.min.js, выгружаемый через npm, может использоваться в качестве ссылки в ваших файлах HTML. Его можно обнаружить в node_modules, например, /home/drequinox/netstats/node_modules/web3/dist/web3.min.js. Этот файл может быть опционально скапирован в тот каталог, в котором находится основное приложение и может применяться оттуда. После того, как на этот файл имеется ссылка в HTML или JS, web3 необходимо инициализировать путём предоставления некоторого поставщика HTTP. Это, как правило, конкретная ссылка на вашу конечную точку HTTP локального хоста, выставляемую исполняемым клиентом geth. Его можно получить воспользовавшись таким кодом:


web3.setProvider(new web3.providers.HttpProvider('http://localhost:8001'));
		

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

Необходимый объект web3 можно создать с помощью такого кода:


if (typeof web3 !== 'undefined')
{
  web3 = new Web3(web3.currentProvider);
}
  else
{
  web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8001"));
}
		

Пример

В следующем разделе будет представлен некий пример, который воспользуется web3.js чтобы сделать возможным взаимодействие с контрактами через веб страницу, обслуживаемую простым веб сервером HTTP. Этого можно достичь, выполнив такие шаги:

  1. Вначале создайте каталог с названием /simplecontract/app в своём домашнем каталоге.

  2. Затем создайте simplecontractcompiled.js как показано здесь:

    
    simplecontractcompiled={
    valueChecker: {
    code:
    "0x6060604052600a60005534610000575b60878061001c6000396000f360606040
    5260e060020a6000350463f9d55e218114601c575b6000565b34600057602960043
    5603d565b604080519115158252519081900360200190f35b6000805460ff83161
    0608157604080516001815290517f3eb1a229ff7995457774a4bd31ef7b13b6f449
    1ad1ebb8961af120b8b4b6239c9181900360200190a15060015b5b91905056",
    info:
    {
    abiDefinition:
    [{
    constant: false,
    inputs:
    [{
    name: "x",
    type: "uint8"
    }],
    name: "Matcher",
    outputs:
    [{
    name: "",
    type: "bool"
    }],
    payable: false,
    type: "function"
    },
    {
    anonymous: false,
    inputs:
    [{
    indexed: false,
    name: "returnValue",
    type: "bool"
    }],
    name: "valueEvent",
    type: "event"
    }],
    compilerOptions: "--combined-json bin,abi,userdoc,devdoc --addstd --optimize",compilerVersion: "0.4.6",
    developerDoc:
    {
    methods: {}
    },
    language: "Solidity",
    languageVersion: "0.4.6",
    source: "pragma solidity ^0.4.0; contract valueChecker { uint
    price=10; event valueEvent(bool returnValue);
    function Matcher (uint8 x) returns (bool) { if (x>=price) {
    valueEvent(true); return true; } } }",
    userDoc: {
    methods: {}
    }
    }
    }
    }
     	   

    Этот файл содержит различные элементы. Наиболее важным является ABI (Application Binary Interface, Двоичный интерфейс приложения), который может опрашиваться с применением geth, как это было показано ранее на шаге 6 в процессе развёртывания самого контракта.

  3. Создайте файл с названием simplecontract.js, как это показано тут:

    
    if (typeof web3 !== 'undefined')
    {
      web3 = new Web3(web3.currentProvider);
    }
    else
    {
      web3 = new Web3(new
      Web3.providers.HttpProvider("http://localhost:8001"));
    }
    console.log("Coinbase: " + web3.eth.coinbase);
    var simplecontractaddress = "0x94a1107f2585f0ab931c71f2f8f02e9f5ab888c0"; 
      simplecontractinstance = web3.eth.contract(simplecontractcompiled.valueChecker.info.abiDefinition).at(simplecontractaddress);
    var code = web3.eth.getCode(simplecontractaddress);
    console.log("Contract balance: " + web3.eth.getBalance(simplecontractaddress));
    console.log("simple contract code" + code);
    function callMatchertrue()
    {
      var txn = simplecontractinstance.Matcher.call(12);{
      };
      console.log("return value: " + txn);
    }
    function callMatcherfalse()
    {
      var txn = simplecontractinstance.Matcher.call(1);{
      };
      console.log("return value: " + txn);
    }
     	   

    Этот файл является основным файлом javaScript, который содержит необходимый код для создания некоего объекта web3. Он также предоставляет методы, которые применяются для взаимодействия с конкретным контрактом в имеющемся блокчейне. Здесь приводятся пояснения данного кода.

Создание объекта web3


if (typeof web3 !== 'undefined')
{
  web3 = new Web3(web3.currentProvider);
}
  else
{
  web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8001"));
}
 	   

Этот код вначале проверяет доступен ли уже какой- нибудь поставщик; если да, тогда он установит имеющегося поставщика в качестве текущего провайдера. В противном случае он установит необходимого поставщика web3 как localhost:8001; это именно то место, в котором исполняется экземпляр geth.

Проверка доступности через вызов любого метода web3


console.log("Coinbase: " + web3.eth.coinbase);
 	   

Эта строка кола просто применяет console.log для печати имеющейся основы монет через вызов метода web3.eth.coinbase. Если этот вывод успешен, это означает, что необходимый объект web3 был создан успешно и HttpProvider доступен. Для проверки такой доступности можно применить любой другой код, однако в качестве простого примера мы воспользовались в своём предыдущем примере web3.eth.coinbase.

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


New-VM -Name VM01 -Generation 2var simplecontractaddress = "0x94a1107f2585f0ab931c71f2f8f02e9f5ab888c0";
 	   

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

Создание основного объекта контракта


simplecontractinstance = web3.eth.contract(simplecontractcompiled
                                 .valueChecker.info.abiDefinition)
                                 .at(simplecontractaddress);
 	   

Этот фрагмент кода создаст некий объект, который будет применяться далее в этом коде для взаимодействия с данным контрактом в имеющемся блокчейне. simplecontractinstance выставит функции данного контракта. web3.eth.contract принимает имеющийся массив ABI в качестве аргумента. Его можно передать через simplecontractcompiled.valueChecker.info.abiDefinition. Нконец, .at принимает необходимый адрес данного контракта в качестве аргумента.

Получение основного кода адреса контракта (не обязательно)

Это показывается здесь просто ради примера и совершенно не обязательно.


var code = web3.eth.getCode(simplecontractaddress);
console.log("simple contract code" + code);
 	   

Предыдущие операторы применяются для опроса самого кода контракта. Это просто пример вызова web3.eth.getCode, который получает установленный адрес данного контракта в имеющемся блокчейне в качестве некоторого аргумента. Наконец, console.log применяется для пкчать самого кода данного контракта через печать переменной кода.

Баланс контракта


console.log(“Contract balance:” +web3.eth.getBalance(simplecontractaddress));
 	   

Предыдущий код вызовет web3.eth.getBalance и получит необходимый адрес контракта в качестве аргумента и напечатает значение баланса данного контракта, которое на данный момент составляет 0.

Функции контракта

После того как объект web3 корректно создан, а также создан simplecontractinstance, запрсото можно выполнять вызовы функций контракта как это показывается в последующем примере:


function callMatchertrue()
{
  var txn = simplecontractinstance.Matcher.call(12);{
  };
  console.log("return value: " + txn);
}

function callMatcherfalse()
{
  var txn = simplecontractinstance.Matcher.call(1);{
  };
  console.log("return value: " + txn);
}
 	   

Вызов может быть осуществлён при помощи simplecontractinstance.Matcher.call и последующей передачи полученного значения для запрошенного аргумента. Повторный вызов имеющейся функции matcher в коде solidity:


function Matcher (uint8 x) returns (bool)
 	   

Он получает аргумент x с типом uint8 и возвращает некое Булево значение, либо истину, либо ложь.

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


var txn = simplecontractinstance.Matcher.call(12);
 	   

В нашем предыдущем примере console.log использовался для печати того значения, которое возвращается данным вызовом функции. Раз полученный результат доступен в нашей переменной txn, он может использоваться везде на протяжении нашей программы, например, в качестве параметра для другой функции JavaScript.

Наконец, создаётся наш HTML файл с названием index.html со следующим кодом:


<html>
<head>
  <title>SimpleContract Interactor</title>
  <script src="./web3.min.js"></script>
  <script src="./simplecontractcompiled.js"></script>
  <script src="./simplecontract.js"></script>
</head>
<body>
  <button onclick="callMatchertrue()">callTrue</button>
  <button onclick="callMatcherfalse()">callFalse</button>
</body>
</html>
 	   

Рекомендуется иметь запущенным некий соответствующий веб сервер чтобы обслуживать необходимое содержимое HTML ( в качестве примера, index.html). В качестве альтернативы это файл можно просмотреть в самой файловой системе, однако это может вызывать определённые проблемы в больших проектах; в качестве хорошей практики всегда применяйте некий веб сервер. Простейший веб сервер в Python может быть запущен приводимой ниже командой. Такой сервер будет обслуживать имеющееся содержимое HTML в том же самом каталоге, из которого он запущен. Python не обязателен; его может заменить даже некий сервер Apache или иной любой веб контейнер.

 

Рисунок 8-32


Простейший веб сервер Python

Теперь можно применять любой браузер для просмотра нашей веб страницы, обслуживаемой по TCP port 7777. Это отображено на следующем примере. Следует отметить, что наш вывод отображён здесь в окне консоли браузера. для просмотра данного вывода должна быть доступна консоль с самим браузером.

 

Рисунок 8-33


Взаимодействие с текущим контрактом

Так как значения вставлены в сам код для упрощения, в index.html созданы две кнопки. Обе эти кнопки вызывают функции с зашитыми в код значениями. Это сделано исключительно для целей демонстрации того, как параметры передаются в наш контракт через web3 и как соответственно возвращаются полученные значения.

Имеются две функции, подлежащие вызову через предоставленные кнопки. Представленный метод callMatchertrue() закодирован со значением 12, которое отправляется в наш контракт при помощи такого кода:


simplecontractinstance.Matcher.call(12)
 	   

Возвращаемое значение печатается в нашей консоли при помощи следующего кода, который вначале вызывает имеющуюся функцию Matcher и затем присваивает полученное значение в установленную переменную txn, подлежащую распечатке позднее в нашей консоли:


simplecontractinstance.Matcher.call(1)
function callMatchertrue()
{
  var txn = simplecontractinstance.Matcher.call(12);{
  };
  console.log("return value: " + txn);
}
 	   

Аналогично работает представленная функция callMatcherfalse() посредством передачи в наш контракт зашитого в код значения 1 применяя:


simplecontractinstance.Matcher.call(1)
 	   

Возвращаемое значение выводится на печать аналогично:


console.log("return value: " + txn);
function callMatcherfalse()
{
  var txn = simplecontractinstance.Matcher.call(1);{
  };
  console.log("return value: " + txn);
}
 	   

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

Инфраструктуры разработки

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

Truffle

Truffle является средой разработки, которая делает её проще и быстрее для проверки и развёртывания контрактов Эфириум. Truffle предоставляет компиляцию контрактов и одновременное подключение с автоматизированной тестовой инфраструктурой при помощи Mocha and Chai. Это также упрощает развёртывание в любую Частную сеть, общедоступный блокчейн или блокчейн Тестовой сети Эфириум. Помимо этого предоставляется полезный конвейер, который упрощает обработку всех файлов javaScript, делая их готовыми к использованию в браузере.

Установка

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


drequinox@drequinox-OP7010:~/testdapp$ nodejs --version
v7.2.1
drequinox@drequinox-OP7010:~/testdapp$ node --version
v7.2.1
		

Установка truffle очень проста и может быть выполнена при помощи следующей команды через npm:


$ sudo npm install -g truffle
		

Это потребует некоторого времени; после того как он установится, можно применить для отображения подсказки и проверки того, что он установлен верно:

 

Рисунок 8-34


Подсказка truffle

В качестве альтернативы доступен репозиторий по ссылке https://github.com/ConsenSys/truffle, который можно клонировть для установки truffle. Для такого клонирования можно применить следующую команду git:


git clone https://github.com/ConsenSys/truffle.git
		

Проверка применения truffle

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


mkdir testdapp
		

Затем перейдите в testdapp и выполните команду:


~/testdapp$ truffle init
		

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


drequinox@drequinox-OP7010:~/testdapp$ tree
.
├── app
│  ├── images
│  ├── index.html
│  ├── javascripts
│  │  └── app.js
│  └── stylesheets
│     └── app.css
├── contracts
│  ├── ConvertLib.sol
│  ├── MetaCoin.sol
│  └── Migrations.sol
├── migrations
│  ├── 1_initial_migration.js
│  └── 2_deploy_contracts.js
├── test
│  └── metacoin.js
└── truffle.js
7 directories, 10 files
		

Эта команда создаёт четыре основных каталога с именамиapp, contracts, migrations и test. Как видно из предыдущего примера, всего было созано 7 каталогов и 10 файлов. В последующих разделах поясняются и будут представлены все эти файлы и каталоги.

  • App: Этот каталог содержит все файлы приложения, включая файлы HTML, образы, таблицы стилей и файлы JavaScript. Данная папка содержит дальнейшие подпапки, images, javascripts и stylesheets, которые содержат отвечающие их названиям файлы приложения.

  • Contracts: Этот каталог содержит файлы исходных кодов контрактов solidity. Именно сюда обращается truffle за контрактами solidity в процессе миграции.

  • Migration: Этот каталог имеет все необходимые сценарии развёртывания.

  • Test: Как и следует из его названия, этот каталог содержит существенные файлы проверки для приложений и котрактов.

Наконец, настройка truffle сохраняется в файле truffle.js, который создаётся в самой корневой папке данного проекта, откуда и исполняется truffle init. Когда truffle init выполнится, он создаст пример проекта с названием MetaCoin. В качестве некоторого упражнения вы вначале рассмотрите как применять различные команды в truffle для тестирования и развёртывания MetaCoin. После этого, дальнейшие примеры покажут как применять truffle в индивидуальных проектах.

Компиляция с помощью truffle

С помощью truffle можно компилировать и библиотеки, и контракты. Ожидается, что название такого контракта будет тем же самым, что и имя самого контракта внутри этого файла. Например, рассматривая созданный ранее пример проекта MetaCoin, мы видим, что файл с названием MetaCoin.sol в каталоге contracts имеет то же самое название, что и контракт MetaCoin в этом файле. Это также применимо и к файлам библиотек, причём это правило чувствительно к регистру символов.

  • Имя файла:

    
    MetaCoin.sol
     	   
  • Название контракта внутри данного файла:

    
    contract MetaCoin {
            mapping (address => uint) balances;
     	   

Компиляция может быть выполнена как показывается здесь:


~/testdapp$ truffle compile
Compiling ConvertLib.sol...
Compiling MetaCoin.sol...
Compiling Migrations.sol...
Writing artifacts to ./build/contracts
~/testdapp$
		

После того, как компиляция успешно завершена, все объекты будут записаны в каталог build. Каталог вывода выглядит как то что показано здесь:


~/testdapp$ tree build/
build/
└── contracts
   ├── ConvertLib.sol.js
   ├── MetaCoin.sol.js
   └── Migrations.sol.js
1 directory, 3 files
		

Как это показано в предыдущем примере, наш каталог build создан автоматически с подкаталогом contracts, который содержит три файла JavaScript.

Миграция

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

Процесс работает как это показано здесь:


~/testdapp$ cd migrations/
~/testdapp/migrations$ ls -ltr
-rw-rw-r-- 1 drequinox drequinox 124 Dec 12 12:57 2_deploy_contracts.js
-rw-rw-r-- 1 drequinox drequinox 72 Dec 12 12:57 1_initial_migration.js
~/testdapp/migrations$ cat 2_deploy_contracts.js
module.exports = function(deployer)
{
 deployer.deploy(ConvertLib);
 deployer.autolink();
 deployer.deploy(MetaCoin);
};
 drequinox@drequinox-OP7010:~/testdapp/migrations$ cat
 1_initial_migration.js
 module.exports = function(deployer)
{
 deployer.deploy(Migrations);
};
		

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

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


module.exports = {
  build: {
    "index.html": "index.html",
    "app.js": [
    "javascripts/app.js"
   ],
   "app.css": [
    "stylesheets/app.css"
    ],
    "images/": "images/"
  },
  rpc: {
    host: "localhost",
    port: 8001
  }
};
 	   

В нашем предыдущем файле rpc необходимо изменить чтобы указывать на соответствующую сетевую среду. После того, как rpc изменён (в нашем примере geth работает по порте 8001, который отличается от обычно применяемого 8545), миграция truffle может быть выполнена с применением приводимой далее команды. Также существенно, что в данном узле Эфириум, на который указывал rpc, будет исполняться майнинг; в противном случае этот контракт невозможно добыть.

Данный контракт может быть развёрнут с помощью показываемой здесь команды:


~/testdapp$ truffle migrate
		

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


Running migration: 1_initial_migration.js
   Deploying Migrations...
Error encountered, bailing. Network state unknown. Review successful
transactions manually.
Error: account is locked
     at Object.InvalidResponse
(/usr/lib/node_modules/truffle/node_modules/etherpudding/node_modules/web3/lib/web3/errors.js:35:16)
     at /usr/lib/node_modules/truffle/node_modules/etherpudding/node_modules/web3/lib/web3/requestmanager.js:86:36
     at exports.XMLHttpRequest.request.onreadystatechange
(/usr/lib/node_modules/truffle/node_modules/web3/lib/web3/httpprovider.js:114:13)
     at exports.XMLHttpRequest.dispatchEvent
(/usr/lib/node_modules/truffle/node_modules/xmlhttprequest/lib/XMLHttpRequest.js:591:25)
     at setState
(/usr/lib/node_modules/truffle/node_modules/xmlhttprequest/lib/XMLHttpRequest.js:610:14)
     at IncomingMessage.
(/usr/lib/node_modules/truffle/node_modules/xmlhttprequest/lib/XMLHttpRequest.js:447:13)
     at emitNone (events.js:91:20)
     at IncomingMessage.emit (events.js:185:7)
     at endReadableNT (_stream_readable.js:974:12)
     at _combinedTickCallback (internal/process/next_tick.js:74:11)
     at process._tickDomainCallback (internal/process/next_tick.js:122:9)
		

С этой учётной записи можно снять блокировку воспользовавшись следующими командами в представленной консоли JavaScript geth.

Вначале выведем перечень всех учётных записей, чтобы посмотреть их все и затем выбрать ту учётную запись, которую требуется разблокировать. Truffle предполагает, что это учётная запись основы монет (coinbase) по умолчанию. Выбираем соответствующую учётную запись следующим образом:


> personal.listAccounts
["0x76f11b383dbc3becf8c5d9309219878caae265c3",
"0xcce6450413ac80f9ee8bd97ca02b92c065d77abc"]
		

Эта учётная запись может быть разблокирована посредством такой команды:


> personal.unlockAccount("0x76f11b383dbc3becf8c5d9309219878caae265c3")
Unlock account 0x76f11b383dbc3becf8c5d9309219878caae265c3
Passphrase:
true
		

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


~/testdapp$ truffle migrate
		

Она отобразит вывод, похожий на представленный ниже. Следует отметить, что майнинг должен быть запущен с тем, чтобы миграция завершилась. Миграция выполнит различные шаги по обнаружению тех файлов, которые доступны в имеющемся каталоге migrations. Как это показано в нашем примере, Для предоставления шагов миграции и требований выполнения truffle применялись 1_initial_migration.js и 2_deploy_contracts.js:


Running migration: 1_initial_migration.js
  Deploying Migrations...
  Migrations: 0xf444cce0cee00cab4d04bcfc0005626b8b02add8
Saving successful migration to network...
Saving artifacts...
Running migration: 2_deploy_contracts.js
  Deploying ConvertLib...
  ConvertLib: 0x2ba8a4a75a6b845bf482923cff29ecc98cd68d90
  Linking ConvertLib to MetaCoin
  Deploying MetaCoin...
  MetaCoin: 0x0be9c5de978fa927b93a5c4faab31312cea5704a
Saving successful migration to network...
Saving artifacts...
~/testdapp$
		

После успешного завершения данной команды она возвратит приглашение командной строки, которое отобразит сообщение saving artefacts.

Данное оснащение можно проверить с применением нескольких показанных тут команд через консоль JavaScript geth:


> eth.getBalance("0x0be9c5de978fa927b93a5c4faab31312cea5704a")
0
> eth.getCode("0x0be9c5de978fa927b93a5c4faab31312cea5704a")
"0x606060405260e060020a60003504637bd703e8811461003457806390b98a111461005657
8063f8b2cb4f1461007d575b610000565b346100005761004460043561009f565b604080519
18252519081900360200190f35b3461000057610069600435602435610119565b6040805191
15158252519081900360200190f35b34610000576100446004356101b1565b6040805191825
2519081900360200190f35b6000732ba8a4a75a6b845bf482923cff29ecc98cd68d906396e4
ee3d6100c4846101b1565b60026000604051602001526040518360e060020a0281526004018
08381526020018281526020019250505060206040518083038186803b156100005760325a03
f415610000575050604051519150505b919050565b600160a060020a0333166000908152602
0819052604081205482901015610142575060006101ab565b600160a060020a033381166000
818152602081815260408083208054889003905593871680835291849020805487019055835
1868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df5
23b3ef929081900390910190a35060015b92915050565b600160a060020a038116600090815
2602081905260409020545b91905056"
		

Отметим, что полученный адрес только что развёрнутого контракта был взят из вывода команды мирации trufflr, который отображён ниже. (MetaCoin: 0x0be9c5de978fa927b93a5c4faab31312cea5704a)

  • Взаимодействие с самим контрактом: Truffle также предоставляет некую консоль (с интерфейсом командной строки), которая позволяет взаимодействовать с имеющимися контрактами. Все развёрнутые контракты уже создали экземпляры и готовы к использованию в данной консоли. Это интерфейс на основе REPL, что означает Read, Evaluate, and Print Loop (Цикл Чтения, Вычисления и Печати). REPL применяется через высталвяемое JSRE (JavaScript runtime environment, Окружение времени исполнения JavaScript). К этой консоли можно получить доступ с помощью следующей команды:

    
    ~/testdapp$ truffle console
    		

    Она открывает показанный здесь интерфейс командной строки:

     

    Рисунок 8-35


    Консоль truffle

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

     

    Рисунок 8-36


    Консоль truffle

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

    
    truffle(default)> MetaCoin.deployed().address
    '0x0be9c5de978fa927b93a5c4faab31312cea5704a'
    truffle(default)>
    		
  • Запрос баланса данного контракта:

    
    truffle(default)>
    MetaCoin.deployed().getBalance.call(web3.eth.accounts[0])
    { [String: '8750'] s: 1, e: 3, c: [ 8750 ] }
    		

    Вывод вернёт строку со значением 8750.

  • Передача значения баланса:

    
    truffle(default)>
    MetaCoin.deployed().sendCoin("0xcce6450413ac80f9ee8bd97ca02b92c
    065d77abc",50, {from:"0x76f11b383dbc3becf8c5d9309219878caae265c3"})
    '0xb8969149fcfb54ec9beac31af1fc86c386f9aa42cb13d2eb9bf9469931986e0f'
    		

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

  • Баланс учётной записи получателя: может быть получен с помощью такой команды:

    
    truffle(default)>
    MetaCoin.deployed().getBalance.call(web3.eth.accounts[1])
    { [String: '1250'] s: 1, e: 3, c: [ 1250 ] }
    truffle(default)>
    		

Для выхода из рассматриваемой консоли truffle используется команда .exit.

Тестирование при помощи truffle

Тестирование является ощной функциональностью truffle и может вызываться исполнением такой команды:


~/testdapp$ truffle test
		

Она считает все тесты из каталога test и выполнит соответствующие проверки. Инфраструктура тестирования, применяемая truffle именуется Mocha и она применяет утверждения инфраструктуры с названием Chai.

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

 

Рисунок 8-37


Вывод проверки truffle, отображающий два успешных теста

Эти два теста основаны на том файле, который производится инициализацией truffle. В приводимом далее файле для простоты показан только один тест, хотя truffle и создаёт три теста для данного проекта по умолчанию MetaCoin. Тесты можно удалить из данного файла отредактировав их файл metacoin.js в текстовом редакторе.


contract('MetaCoin', function(accounts)
{
  it("should put 10000 MetaCoin in the first account",
  function()
  {
    var meta = MetaCoin.deployed();
    return meta.getBalance.call(accounts[0]).then(function(balance)
    {
      assert.equal(balance.valueOf(), 10000, "10000 wasn't in the first account");
    });
  });
});
 	   

Все тестовые файлы должны быть представлены в каталоге tests из каталога project. Тесты определяются в рамках блоков it.

 

Рисунок 8-38


Тестирование на основе показанного ранее файла с единственным тестом

В предыдущем варианте проверки, когда представлен данный контракт, он должен иметь внутри баланс 10 000. Данный тест в основном проверяет доступен ли баланс 10 000 после развёртывания данного контракта или нет. Чтобы пояснить саму концепцию, можно имитировать определённую ошибку, например, если файл metacoin.js изменён следующей подменой


assert.equal(balance.valueOf(), 10000, "10000 wasn't in the first account");
 	   

на


assert.equal(balance.valueOf(), 1000, "10000 wasn't in the first account");
 	   

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

 

Рисунок 8-39


Вывод неудачной проверки truffle

Команда truffle test принимает ряд необязательных параметров, в особенности, --verbose-rpc, который может быть очень полезным для понимания взаимодействия между данным клиентом Эфириум и truffle.

Порой при исполнении проверок может появляться некое сообщение об ошибке, подобное приводимому ниже:


Error: timeout of 120000ms exceeded. Ensure the done() callback is being called in this test.
		

Эта ошибка происходит, когда либо сам узел Эфириум не майнится, либо развёртывание данного контракта осуществляется более двух минут. Оно выдаётся при наступлении таймаута; поэтому важно чтобы проверки выполнялись через некий узел майнинга в случае Частной сетевой среды. Кроме того, в Ropsten это иногда может занимать более 2 минут. В качестве альтернативы можно применять ethereumjs-testrpc, который обычно применяется с truffle и предоставляет быструю эмуляцию RPC клиента Эфириум.

Построение

Построение применяется в truffle для начальной загрузкиинтерфейса браузеров. Оно работает путём импорта скомпилированных и важных развёрнутых контракто, а также настроек клиента Эфириум. Все объекты после построения сохраняются в имеющемся каталоге ./build. Все построенные конфигурации представлены в основном файле truffle.js, который руководит truffle относительно того как осуществлять построение. По умолчанию данный файл поступает только с конфигурациями build: и rpc:.

Построение может бфть запущено такой командой:


~/testdapp$ truffle build
		

После завершения построения будет создан каталог build, если его пока не имеется и будет выработана древовидная структура, аналогичная показанной ниже. Всё это содаётся на основании файла truffle.js:


build/
├── app.css
├── app.js
├── contracts
│  ├── ConvertLib.sol.js
│  ├── MetaCoin.sol.js
│  └── Migrations.sol.js
├── images
└── index.html
		

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

Отметим, что данная команда исполняется с флагом -p для определения порта TCP 7777. Это необходимо так как geth, в приведённом перед этом примере, исполняется с опцией --rpccorsdomain 'http://localhost:7777'. Это означает, что доступно всё содержимое, обслуживаемое через TCP 7777. По умолчанию serve обслуживает порт 8080, который может быть занят каким- то иным процессом в данной системе, так как TCP 8080 очень распространённый порт для веб приложений.

 

Рисунок 8-40


Обработка truffle

После того как данный сервер truffle поднят и исполняется с соотвествующим значением порта, наше содержимое может рассматриваться в браузере и указываться URL http://localhost:7777.

 

Рисунок 8-41


Пример интерфейса MetaCoin

Другой пример

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

  1. Созайте некий католг с названием simpleTest:

    
    $ mkdir simpleTest
     	   
  2. Инициализируйте truffle:

    
    $ truffle init
     	   
  3. Удалите файлы из каталогов. это необходимо чтобы удалить все созданные truffle по умолчанию файлы проекта MetaCoin.

    
    rm -r test/* contracts/* migrations/*
     	   
  4. Поместите два файла Addition.sol и Migrations.sol в каталог контрактов:

    
    Addition.sol:
    
    pragma solidity ^0.4.2;
    contract Addition
    {
      uint8 x;
      function addx(uint8 y, uint8 z )
      {
        x = y + z;
      }
      function retrievex() constant returns (uint8)
      {
        return x;
      } }
    
    Migrations.sol:
    pragma solidity ^0.4.2;
    contract Migrations
    {
      address public owner;
      uint public last_completed_migration;
      modifier restricted()
      {
        if (msg.sender == owner) _;
      }
      function Migrations()
      {
        owner = msg.sender;
      }
      function setCompleted(uint completed) restricted
      {
        last_completed_migration = completed;
      }
      function upgrade(address new_address) restricted
      {
        Migrations upgraded = Migrations(new_address);
        upgraded.setCompleted(last_completed_migration);
      }
    }
     	   
  5. Поместите файл Addition.js в каталог test:

    
    contract('Addition', function(accounts)
    {
      it(" 100 + 100 = 200 ", function()
      {
        var AddContract = Addition.deployed();
        AddContract.addx(100, 100,{from:accounts[0],gas:1000000})
    	.then(function(a)
        {
          return AddContract.retrievex.call().then(function(Result)
          {
            assert.equal(Result, 200, "100 + 100 = 200 is expected");
          });
        });
      });
    });
     	   
  6. В своей папке migrations разместите два файла:

    
    1_initial_migration.js:
    
    module.exports = function(deployer)
    {
      deployer.deploy(Migrations);
    };
    
    2_deploy_contracts.js:
    
    module.exports = function(deployer)
    {
      deployer.deploy(Addition);
    deployer.autolink();
    };
     	   
  7. Когда все файлы окажутся на своих местах, скомпилируйте все контракты при помощи компилятора truffle. Опционально можете применить флаг --compile-all чтобы повторно скомпилировать имеющиеся контракты, даже если они уже были откомпилированы. Он необходим только в случае, если все контракты нуждаются в повторной компиляции:

    
    ~/simpleTest$ truffle compile
    Compiling Addition.sol...
    Compiling Migrations.sol...
    Writing artifacts to ./build/contracts
     	   
  8. Выполните миграцию в тестовую сетевую среду Эфириум при помощи миграции truffle. Это развернёт данный контракт в имеющейся сетевой среде. Отметим, что на данный момент truffle.js следует обновить вновь с портом 8001, чтобы указывать на представленную Частную сеть:

    
    ~/simpleTest$ truffle migrate
    Running migration: 2_deploy_contracts.js
      Deploying Addition...
      Addition: 0x73934227a1ce7fc44152b7451626759a00b0275c
    Saving successful migration to network...
    Saving artifacts...
     	   

    Наконец, можно выполнить проверку с помощью показанной ниже команды. Эти тесты основаны на том файле Addition.js, который был показан ранее:

    
    ~/simpleTest$ truffle test
     	   

    Данная команда вначале развернёт сам контракт в сетевую среду Эфириум (в данном примере Частная сеть).

     

    Рисунок 8-42


    Пример вывода, показывающий успешную проверку truffle

  9. Для взаимодействия с данным контрактом можно применять все показанные далее методы. Так как данный контракт Addition уже установил экземпляр и доступен в консоли truffle, становится достаточно просто взаимодействовать с этим контрактом применяя различные методы.

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

    
    truffle(default)> Addition.address
    '0x73934227a1ce7fc44152b7451626759a00b0275c'
     	   

    Для вызова определённых функций в пределах данного контракта в функциях контракта применяются уже развёрнутые методы. Здесь показывается пример, в котором вызывается функция addx и передаются два параметра:

    
    truffle(default)> Addition.deployed().addx(100,100)
    '0xae6f51782c1bcf04ec34dd54ee31da626dc138993ea813bc6c3c1fe0790b130e'
    truffle(default)>
    '0xb9f8633fbd626466ee2c2f24952a5fca3134f4e7d08f39a4d26ac2689e22b653'
     	   

    Вызовите из данного контракта функцию retrievex:

    
    truffle(default)> Addition.deployed().retrievex()
    { [String: '200'] s: 1, e: 2, c: [ 200 ] }
     	   

Пример проекта: Подтверждение идеи

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

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

Например, будут применены браузер solidity, truffle и Тестовая сетевая среда (уже работающая Сетевая среда с идентификатором 786, созданная ранее). Вначале будет написан необходимый код для нужного нам контракта. Это можно сделать при помощи подходящего текстового редактора или в интегрированной среде разработки. Также можно воспользоваться браузером solidity, так как он тоже предоставляет для тестирования некую среду эмуляции. Данный пример снабдит вас возможностью узычить как некий контракт может быть развит от какой- то идеи в исходный код solidity и окончательно в какое- то оснащение.

Давайте рассмотрим даннй код строку за строкой:


pragma solidity ^0.4.0;
 	   

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


contract PatentIdea {
 	   

Данный оператор является началом нашего контракта с названием PatentIdea.


mapping (bytes32 => bool) private hashes;
 	   

Затем определяется некое соответствие, которое устанавливает соответствие byte32 Булеву значению и именно это является базовым соответствием hashmap (словарём) bytes32 отображаемым в устанавливаемое Булево значение.


bool alreadyStored;
 	   

Это объявление переменной с названием alreadyStored, которая имеет Булев тип и может иметь значение Истина или Ложь. Данная переменная применяется для хранения возвращаемого функцией SaveIdeaHash значения.


event ideahashed(bool);
 	   

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

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


function saveHash(bytes32 hash) private
{
  hashes[hash] = true;
}
 	   

Определяется другая функция, saveIdeaHash и она принимает значение переменной idea со строковым типом и возвращает некое Булево значение (Истина или Ложь) в зависимости от результата данной функции:


function SaveIdeaHash(string idea) returns (bool)
{
  var hashedIdea = HashtheIdea(idea);
  if (alreadyHashed(HashtheIdea(idea)))
  {
    alreadyStored=true;
    ideahashed(false);
    return alreadyStored;
  }
  saveHash(hashedIdea);
  ideahashed(true);
}
 	   

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

Следующей функцией является alreadyHashed, которая определяется для получения значения переменной именованного хэша с типом byteы32 и возвращает некое Булево значение (Истина или Ложь) после проверки данного хэша с имеющейся картой хэшей. Она опять- таки определяется как константа, а доступ к ней определяется как частный:


function alreadyHashed(bytes32 hash) constant private returns(bool)
{
  return hashes[hash];
}
 	   

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


function isAlreadyHashed(string idea) constant returns (bool)
{
  var hashedIdea = HashtheIdea(idea);
  return alreadyHashed(hashedIdea);
}
 	   

Наконец, здесь показывается функция HashtheIdea, которая принимает значение переменной idea со строковым типом и является типом constant, что означает что она не может изменить значение состояния данного контракта. Она также определяется как private, так как она используется только внутри данного контракта. Данная функция возвращает значение с типом bytes32:


function HashtheIdea(string idea) constant private returns (bytes32) 
{
  return sha3(idea);
}
 	   

Эта функция вызывает встроенную в solidity функцию sha3 и передаёт некую строку в ней в качестве переменной idea. Данная функция возвращает хэш sha3 данной строки. Название функции sha3 является неким сокращением для доступной в solidity функции keccak256(), которая вычисляет хэш кечак-256 переданной в неё строки. Отметим, что она не является стандартом NIST для SHA-3, а вместо этого реализует кечак-256, который первоначально был предложен в NIST для испытания в качестве создаваемого стандарта SHA-3. Он был позднее слегка изменён и выпущен в качестве стандарта SHA-3 NIST. Реальная функция стандарта хэширующей функции SHA-3 будет возвращать значения хэшей, отличающиеся от получаемых кечак-256 (функция Эфириум sha3).

Здесь приводится полный исходный код контракта:


pragma solidity ^0.4.0;
contract PatentIdea
{
  mapping (bytes32 => bool) private hashes;
  bool alreadyStored;
  event ideahashed(bool);
  function saveHash(bytes32 hash) private
  {
    hashes[hash] = true;
  }
  function SaveIdeaHash(string idea) returns (bool)
  {
    var hashedIdea = HashtheIdea(idea);
    if (alreadyHashed(HashtheIdea(idea)))
    {
      alreadyStored=true;
      ideahashed(false);
      return alreadyStored;
    }
    saveHash(hashedIdea);
    ideahashed(true);
  }
  function alreadyHashed(bytes32 hash) constant private returns(bool)
  {
    return hashes[hash];
  }
  function isAlreadyHashed(string idea) constant returns (bool)
  {
    var hashedIdea = HashtheIdea(idea);
    return alreadyHashed(hashedIdea);
  }
  function HashtheIdea(string idea) constant private returns (bytes32)
  {
    return sha3(idea);
  }
}
 	   

Этот исходный код можно эмулировать в браузере solidity чтобы проверить что он работает правильно. Некоторые примеры показаны тут.

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

 

Рисунок 8-43


Создание контракта с применением браузера solidity

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

После клика по Create будут выставлены две функции из данного контракта, как это отображено на следующем снимке экрана:

 

Рисунок 8-44


Соответствующие затраты и представление двух методов

Функции можно вызывать как это показано в следующем примере:

 

Рисунок 8-45


Вызов определённой функции

Аналогично можно вызвать функцию isAlreadyHashed.

 

Рисунок 8-46


Исполнение функции isAlreadyHashed

Если та же самая строка передаётся в данную функцию снова, она не будет сохранена, как это демонстрирует следующий снимок экрана:

 

Рисунок 8-47


Исполнение функции SaveIdeaHash

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

После того, как необходимый контракт написан и выполнена его эмуляция в браузере solidity, нашим следующим шагом является применение truffle для инициализации какого- то нового проекта и его развёртывания и проверки в нашей Частной сети (с идентификатором 786), уже созданной в предыдущих разделах.

Первым шагом является создание отдельного каталога для нашего проекта:


~$ mkdir ideapatent
~$ cd ideapatent/
		

Следующий этап заключается в инициализации truffle и создании нового проекта:


~/ideapatent$ truffle init
		

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


~/ideapatent/contracts$ rm MetaCoin.sol ConvertLib.sol
		

В нашей папке contracts создайте файл с названием PatentIdea.sol и поместите в него тот исходный код, который был показан ранее.

Измените truffle.js чтобы он указывал на терминал HTTP локального хоста:


rpc:
  {
    host: "localhost",
    port: 8001
  }
		

В папке ~/ideapatent/migrations folder измените свой файл 2_deploy_contracts.js с тем, чтобы он выглядел следующим образом:


module.exports = function(deployer)
{
  deployer.deploy(PatentIdea);
  deployer.autolink();
};
		

Этот файл изменяется чтобы определить то имя контракта, который подлежит развёртыванию. Обратите внимание на deployer.deploy(PatentIdea);.

Затем выполните компиляцию при помощи truffle, как это показано здесь:


~/ideapatent$ truffle compile
Compiling Migrations.sol...
Compiling PatentIdea.sol...
Writing artifacts to ./build/contracts
		

Обеспечьте запуск майнинга в фоновом режиме и развёртывание в необходимую сетевую среду как показано далее:


~/ideapatent$ truffle migrate
Running migration: 1_initial_migration.js
  Deploying Migrations...
  Migrations: 0x34d63de23de9c9b48251cec94fff427b94976109
Saving successful migration to network...
Saving artifacts...
Running migration: 2_deploy_contracts.js
  Deploying PatentIdea...
  PatentIdea: 0x515fd6a5dbc1eb609dc1700f73be040d9db50d4b
Saving successful migration to network...
Saving artifacts...
		

После того, как данный контракт развёрнут, с ним можно осуществлять взаимодействие из консоли truffle.

Запустите необходимую консоль truffle, вызвав данную команду:


~/ideapatent$ truffle console
		

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

Например, зарегистрируйте новую идею:


truffle(default)> PatentIdea.deployed().SaveIdeaHash("MyIdea")
'0x8644dc66f1173a9103034e17b761f8871ab10ef2a7d19bec9c7eb7164272b8a3'
		

Убедитесь что MyIdea хэширована:


truffle(default)> PatentIdea.deployed().isAlreadyHashed("MyIdea")
true
		

Проверьте можно ли хэшировать другую идею, или нет:


truffle(default)> PatentIdea.deployed().isAlreadyHashed("MyOtherIdea")
false
truffle(default)>
		

Данный пример демонстрирует как может быть создан некий контракт в рабочей области, как его эмулировать и как развернуть его в имеющейся частной сети. Чтобы развернуть его в Тестовой сетевой среде (Ropstern) или в блокчейне реального времени, необходимо выполнить подобные упражнения. Просто укажите надлежащий RPC и воспользуйтесь миграцией truffle для развёртывания в блокчейне по вашему выбору.

В наших последующих разделах будут обсуждены некоторые расширяющие понятия.

Оракулы

Как обсуждалось в Главе 6, Адаптивные контракты, Оракулы - это реальные каналы передачи данных в адаптивные контракты (smart contracts). Имеется множество доступных служб для предоставления Ракулов адаптивным контрактам. Наиболее выдающимся является Oracleze, который доступен на http://www.oraclize.it/. Это в особенности полезно, если имеющимся адаптивным контрактам необходимы, скажем, фрагменты из интернета из исходных кодов сторонних производителей или иных данных реального мира, например, погодных условий в определённом городе. Имеется множество вариантов применения, в которых Оракулы могут предоставлять достоверные каналы данных в адаптивные контракты чтобы позволить им принимать решения в соответствии с событиями из реальной обстановки. Оракулы делают более простым для адаптивных контрактов выполнять доступ в Интернет чтобы получать требующиеся данные.

Чтобы использовать Oraclize в Эфириуме необходимо отправить транзакцию в такой контракт с Oraсlize совместно необходимым платежом и соответствующим запросом. Как результат, Oraclize будет выбирать необходимые результаты на основании тех запросов, которые предоставляются в транзакции обращения и отправляться обратно по адресу самого контракта. После того, как данная транзакция отправлена назад в свой контракт, будет вызван метод обратного вызова (call-back) или функция перехода на нижний уровень (fall back).

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

Обработка Oraclize модно изобразить как это показано в следующей схеме:

 

Рисунок 8-48


Поток данных Oraclize

Структура скелета некоторого контракта solidity с применением Oraclize выглядит как тот, что показан здесь. Отметим, что импорт работает только в тех средах разработки, которые предоставляются в таком Web со стороны Oraclize; обычно это файл необходимо импортировать вручную:


import "dev.oraclize.it/api.sol";
contract MyOracleContract is usingOraclize
{
  function MyOracleContract(){
}
		

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


oraclize_query("URL", "api.somewebsite.net/price?stock=XYZ");
		

Oraclize может также применять заверение TLS чтобы гарантировать что канал безопасен и скорее всего не фальсифицирован.

Развёртывание в децентрализованном хранилище при помощи IPFS

Как уже обсуждалось в Главе 1, Азы блокчейн, чтобы получать полные преимущества от платформ децентрализации, желательно чтобы вы выполнили децентрализацию своих системы хранения и уровня взаимодействия. Обычно всё содержимое веб обслуживается через централизованные серверы, однако эта часть также может быть децентрализована с применением распределённых файловых систем.

Содержимое HTML, отображённое в представленных ранее примерах, может храниться распределённо и в децентрализованных сетевых средах IPFS для достижения расширенной децентрализации.

IPFS доступен по адресу https://ipfs.io/.

Установка IPFS

IPFS может быть установлен путём следующего процесса:

  1. Выгрузка пакета IPFS применяет следующую команду:

    
    $ curl https://dist.ipfs.io/go-ipfs/v0.4.4/go-ipfs_v0.4.4_linux-amd64.tar.gz -O
     	   
  2. Раскрутите это файл gz:

    
    $ tar xvfz go-ipfs_v0.4.4_linux-amd64.tar.gz
     	   
  3. переместите полученный файл ipfs в соответствующую папку чтобы сделать её доступной в установленном пути:

    
    $ mv go-ipfs/ipfs /usr/local/bin/ipfs
     	   
  4. Установите необходимый узел IPFS:

    
    imran@drequinox-OP7010:~$ ipfs init
    initializing ipfs node at /home/imran/.ipfs
    generating 2048-bit RSA keypair...done
    peer identity: Qmbc726pLS9nUQjUbeJUxcCfXAGaXPD41jAszXniChJz62
    to get started, enter:
            ipfs cat /ipfs/QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG/readme
     	   
  5. Введите следующую команду, чтобы гарантировать что IPFS был успешно установлен:

     

    Рисунок 8-49


    Успешная установка IPFS

  6. Запустите необходимый демон IPFS:

    
    imran@drequinox-OP7010:~$ ipfs daemon
    Initializing daemon...
    Swarm listening on /ip4/127.0.0.1/tcp/4001
    Swarm listening on /ip4/192.168.0.17/tcp/4001
    Swarm listening on /ip4/86.15.44.209/tcp/4001
    Swarm listening on /ip4/86.15.44.209/tcp/41608
    Swarm listening on /ip6/::1/tcp/4001
    API server listening on /ip4/127.0.0.1/tcp/5001
    Gateway (readonly) server listening on /ip4/127.0.0.1/tcp/8080
    Daemon is ready
     	   
  7. Скопируйте файлы в IPFS при помощи таких команд:

    
    ~/sampleproject/build$ ipfs add --recursive --progress .
    added QmVdYdY1uycf32e8NhMVEWSufMyvcj17w3DkUt6BgeAtx7
    build/app.css
    added QmSypieNFeiUx6Sq7moAVCsgQhSY3Bh9ziwXJAxqSG5Pcp
    build/app.js
    added QmaJWMjD767GvuwuaLpt5tck9dTVCZPJa9sDcr8vdcJ8pY
    build/contracts/ConvertLib.sol.js
    added QmQdz9eG2Qd5kwaU86kWebDGPqXBWj1Dmv9MN4BRzt2srf
    build/contracts/MetaCoin.sol.js
    added QmWpvBjXTP4HutEsYUh3JLDi8VYp73SKNJi4aX1T6jwcmG
    build/contracts/Migrations.sol.js
    added QmQs7j6NpA1NMueTXKyswLaHKq3XDUCRay3VrC392Q4JDK
    build/index.html
    added QmPvWzyTEfLQnozDTfgdAAF4W9BUb2cDq5KUUrpHrukseA
    build/contracts
    added QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn
    build/images
    added QmSxpucr6J9rX3XQ3MBG8cVzLCrQFFKmMkTmpcNpjbtf3j build
     	   
  8. Теперь к нему можно получить доступ в браузере следующим образом:

     

    Рисунок 8-50


    Доступ браузера к веб страницам через IPFS

    [Замечание]Замечание

    Отметим, что даннй URL указывает на саму файловую систему IPFS.

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

    
    /build$ ipfs pin add QmSxpucr6J9rX3XQ3MBG8cVzLCrQFFKmMkTmpcNpjbtf3j
    pinned QmSxpucr6J9rX3XQ3MBG8cVzLCrQFFKmMkTmpcNpjbtf3j recursively
     	   

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

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

Собственный протокол Эфириум также находится в интенсивной разработке и работает на основе аналогичных принципов. Однако, Swarm в настоящее время Swarm пребывает в разработке, а IPFS является более разработанным при их сравнении и выглядит лучшим выбором на данный момент. IPFS работает очень хорошо и может стать хорошим вариантом необходимой платформы для развёртывания децентрализованных развёртываний. Swarm позволяет пользователям исполнять лёгкие клиенты с сохранением в нём всех необходимых данных блокчейна. Он доступен в текущей версии geth, а подробное руководство доступно по ссылке https://swarm-guide.readthedocs.io/en/latest/introduction.html. Так как он пребывает в интенсивной разработке, для этой технологии приводится лишь лёгкое введение, так как очень вероятно её быстрое изменение по мере развития..

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

Разрешённые распределённые книги учёта

Концепция распределённых книг учёта кардинально отличается от общедоступного блокчейна. Основная идея распределённых книг учёта состоит в том, что они получают разрешение в отличие от общедоступного блокчейна. DLT не выполняет никакого майнинга, так как все участники уже проверены и известны в имеющейся сетевой среде и не нет потребности в каком- то майнинге для безопасности данной сетевой среды. Также отсутствует какая бы то нибыло концепция цифровой валюты в частных разрешённых книгах учёта, так как основная цель данного разрешённого блокчейна отличается от цели общедоступного блокчейна. В неоктором общедоступном блокчейне доступ открыт для всех и требует некоторого вида стимулирования и следствия сети для роста; в противоположность этому в разрешённых DLT такие требования отсутствуют. Имеется возможность построения разрешённого DLT с применением Эфириум в установках частного консорциума, и особенно для работы внутри имеющихся финансовых систем. Основным ключевым преимуществом распределённых систем книг учёта состоит в том, что они намного быстрее, хорошо управляемы и имеют возможность взаимодействия с уще существующими финансовыми системами.

Выводы

Данная глава предоставила подробные и глубокие практические примеры того, как устанавливать среду разработку Эфириум и создавать адаптивные контракты (smart contract). Данная глава начинается с некоторого введения в различные методы, которые могут применяться для создания Частных сетевых сред Эфириумдля тестирования и целей разработки. После этого было представлено некое введение в язык программирования solidity с тем, чтобы позволить вам понимать основы этого языка и его синтаксис. Подробно обсуждались практические техники развёртывания с применением техник и инструментов, как geth и web3. Кроме этого были представлены подробные пошаговые примеры разработки адаптивных контрактов и развёртывания. Кроме того обсуждались инфраструктуры развёртывания с практическими примерами с тем, чтобы вы получили опыт жизненного цикла разработки определённого адаптивного контракта в имеющемся блокчейне Эфириум. Это длинная глава и, если тесно следовать за упражнениями, вы получите глубокое понимание разработки контрактов, их проверки и развёртывания в Эфириум. Наконец, обсуждались различные понятия и инструменты, относящиеся к децентрализованному хранению, децентрализованному взаимодействию и Оракулам. Так как Эвириум и связанные с ним технологии и инфраструктуры находятся в постоянном и быстром развитии, представляется,что более продвинутые инструменты и техники появятся со временем; однако обсуждаемые в данной главе основы, скорее всего, останутся прежними. Кроме того, нет возможности обсуждать в этой главе все без исключения инструменты и инфраструктуры доступные в Эфириум, однако все обсуждаюемые инструменты и техники в применении основного потока и должны представить солидную основу для вас чтобы перейти к последующему уровню. Имеются некоторые темы, которые специально не обсуждались в данной главе, такие как безопасность адаптивных контрактов, формальная проверка адаптивных контрактов, блокчеёны как службы в облаке и определённые варианты применения адаптивных контрактов для различных отраслей. Все эти понятия будут обсуждаться в последующих главах. Я надеюсь, что вы получили удовольствие при чтении данной главы настолько же, насколько мне понравилось её писать.