Глава 8. Разработка Эфириума
Содержание
Эта глава вводит основные понятия, технологии и инструменты, относящиеся к разработке Эфириум. В этой главе будут представлены некоторые примеры дополнительно к теоретическим понятиям, приводившимся в предыдущих главах. Эта глава в основном охватывает установку среды разработки и то, как создавать адаптивные контракты (smart contract) с применением блокчейна Эфириум. Будет представлен подробный обход примеров, который поможет вам понять то, как Эфириум и прочие инструменты поддержки могут применяться для разработки и оснащения различных адаптивных контрактов в данном блокчейне.
Первой важной задачей является установка некоторой среды разработки. Последующие разделы представят установку Эфириум для Тестовой сети и Частной
сети. тестовая сеть, имеющая название Ropsten, применяется разработчиками и пользователями в качестве тестовой платформы для проверки адаптивных контрактов
и прочих относящихся к блокчейну предложений. Опция Частной сети в Эфириум делает возможным создание независимой частной сети, которая может применяться
как распределённая система регистрации между определёнными юридическими лицами, а также для разработки и проверки адаптивных контрактов. Хотя и имеются
прочие клиенты в Эфириум, такие как Parity, которые обсуждались в предыдущей главе, geth является ведущим клиентом для Эфириум и обычно выбираемым
инструментом, следовательно данная глава будет применять в качестве примеров geth
.
Клиент Эфириум Go, geth
, может быть подключён к сетевой среде тестирования при помощи следующей
команды:
$ geth --TestNet
Простой вывод отображён на приводимом далее снимке. Этот снимок экрана отображает тот тип сетевой среды, который выбран, а также различные прочие части информации, относящиеся к выгрузке существующего блокчейна.
Проводник блокчейна под тестирование размещается по адресу https://testnet.etherscan.io/ и может применяться для отслеживания транзакций и блоков в тестовой сетевой среде Эфириум.
Частная сеть делает возможным создание совершенно нового блокчейна. Она отличается от Тестовой сети или Основной сети в том смысле, что она использует свои блок порождения Идентификатор сети. Для создания Частной сетевой среды требуются три компонента:
-
Идентификатор сетевой среды.
-
Файл порождения.
-
Каталог данных для сохранения информации блокчейна. Даже несмотря на то, что каталог данных не является строго необходимым для упоминания, если существует более чем один уже активный блокчейн в вашей системе, тогда каталог данных следует определить с тем, чтобы для выстраиваемого нового блокчейна применялся некий отдельный каталог.
Частная сетевая среда делает возможным создание какого- то целиком нового блокчейна. Это отличает её от Тестовой сети и Основной сети в том смысле, что она применяет свой собственный уникальный блок порождения и Идентификатор сети. В основной сети 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
Она произведёт вывод, аналогичный приведённому на следующем снимке экрана:
Данный вывод указывает, что некий блок порождения был успешно создан. Чтобы запустить geth
, необходимо
вызвать такую команду:
$ geth --datadir .ethereum/privatenet/ --networkid 786
Она осуществит следующий вывод:
Теперь к geth
можно подключаться через IPC для исполнения клиента geth в частной сети с помощью
приводимой далее команды. Это позволит нам осуществлять взаимодействие посредством выполнения сеанса geth
в данной частной сетевой среде:
$ geth attach ipc:.ethereum/privatenet/geth.ipc
Как показывается здесь, она откроет необходимую консоль взаимодействия JavaScript для выполнения сеанса Частной сети:
Вы можете отметить, что при запуске 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 и осуществляется вывод, подобный следующему:
Когда генерация DAG завершится и майнинг запущен, geth
осуществит вывод подобный отображаемому на
снимке экрана внизу. Из сообщения Mined 5 blocks . . .
можно явно увидеть, что блоки добываются
успешно.
майнинг можно остановить следующей командой:
> miner.stop
true
В имеющейся консоли JavaScript может быть опрошено состояние текущего баланса общего объёма эфира, как это показывается ниже. После майнинга можно увидеть некое существенное количество в нашем следующем примере. В частной сети и в файле порождения майнинг является чрезвычайно быстрым, к тому же уровень сетевой сложности также был достаточно низок:
> eth.getBalance(eth.coinbase).toNumber();
2.72484375e+21
Если в последовательности присутствуют два пробела и две табуляции, будет отображён полный перечень всех доступных объектов. Он отображается в приводимом далее снимке экрана:
Затем, после набора команды, она может быть автоматически выполнена двойным нажатием табуляции. Если нажаты две табуляции, тогда также отобразится полный перечень доступных методов. Это показано на следующем снимке экрана:
Дополнительно к упомянутым ранее командам, чтобы получить список доступных методов, после наборы любой команды воодится точка с запятой (;).
На снимке экрана внизу приводится некий пример, который отображает список всех методов, доступных для
net
:
Имеется несколько других команд, которые могут применяться для опроса имеющейся частной сети. Несколько примеров приводится далее:
-
Получить текущую стоимость газа:
> 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.
После того, как Эфириум запущен, будет отображён тот интерфейс, который показывается ниже, что ясно указывает на то он исполняется в режиме PRIVATE-NET.
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 в его кнопке данного экрана.
запросит пароль данной учётной записи и отобразит некое окно, аналогичное показанному ниже снимку экрана:
введите требующийся пароль и кликните по SEND TRANSACTION для развёртывания данного контракта.
После успешного развёртывания и майнинга, он появится в полном перечне транзакций в mist, как показано тут:
После того, как этот контракт становится доступным, с ним можно осуществлять взаимодействие при помощи исполнения транзакции и вызова доступных функций в Mist.
В нашем предыдущем снимке экрана доступны опции READ FROM CONTRACT и WRITE TO CONTRACT. Кроме того, с правой стороны можно видеть те функции, которые выставляются самим контрактом. После выбора требуемой функции, вводится соответствующее значение для данной функции и выбирается значение учётной записи (от имени которой необходимо исполнение); чтобы исполнить данную транзакцию нажмите execute, что в результате вызовет выбранную функцию данного контракта.
Весь процесс отображён на снимке экрана:
Как отображено на данном снимке экрана, ввод соответствующего пароля с последующим нажатием SEND TRANSACTION отправляют необходимую транзакцию в рассматриваемый контракт.
Имеется целый ряд инструментов доступных для разработки Эфириум. Следующая схема показывает соподчинение различных инструментов, клиентов, IDE и инфраструктур разработки для Эфириум:
В данной главе мы в основном сосредоточимся на 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 поддерживает различные функции. Ниже приводятся некоторые примеры:
-
Отобразить контракт в двоичном виде.
-
Оценка газа:
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. Ниже приводится пример интерфейса:
С левой стороны имеется редактор кода с подсветкой синтаксиса и форматированием текста, а с правой стороны представлен ряд инструментов,
для их применения в развёртывании, отладке, тестировании и взаимодействии с определённым контрактом. Доступны различные функции, такие как
взаимодействие с транзакцией, вариант подключения к ВМ 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
.
Если эта команда успешна и веб сервер исполняется, remix можно просматривать с применением URL http://localhost:7777, что показано на снимке экрана внизу:
Remix также доступен в качестве части браузера solidity (браузер solidity будет обсуждён отдельно позднее). К нему можно подключиться через локальную Частную сетевую среду, выдающую терминал web3 поставщика. Это можно продемонстрировать так:
Опция Web3 Provider в браузере solidity для локального узла
geth
выглядит следующим образом:
Инструменты и библиотеки
В Эфириум доступны различные инструменты и библиотеки. Наиболее употребимые обсуждаются здесь.
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 для проводника Эфириум:
Когда веб сервер поднят, geth
следует запустить такой командой:
geth --datadir .ethereum/privatenet/ --networkid 786 --rpc --rpccorsdomain 'http://localhost:8000'
После успешного запуска geth
, в локальном хосте переместитесь на TCP
port 8000
, как это показано здесь, чтобы получить доступ к имеющемуся локальному проводнику блока
Эфириум.
В качестве альтернативы, необходимый веб сервер может быть запущен с применением Python или любого другого соответствующего поставщика. В Python простейший веб сервер может быть запущен при помощи следующего кода:
imran@drequinox-OP7010:~/explorer/app$ python -m SimpleHTTPServer 9900
Serving HTTP on 0.0.0.0 port 9900 ...
Наш клиент geth
необходимо запустить с соответствующими параметрами. Если это будет не так, может
отобразиться ошибка подобная показанной на снимке экрана:
Перезапустите 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 является подходящим для данной области язык программирования контрактов в Эфириум. Тем не менее, имеются и прочие языки, такие как Mutan и LLL, однако solidity является наиболее популярным на момент написания данных строк. Его синтаксис близок к JavaScript и C. Solidity на протяжении последних нескольких лет превратился в зрелый язык и его достаточно просто применять, но ему ещё предстоит долгий путь, прежде чем он станет отлаженным и насыщенным функциональностью как и прочие хорошо зарекомендовавшие себя языки. Тем не менее, именно он в настоящее время является наиболее распространённым языком программирования контрактов.
Он относится к языкам со статическими типами, что означает что проверка типа переменной в solidity осуществляется на момент компиляции. Всякая переменая, будь она статичной или локальной, должна быть определена с неким типом на момент компиляции. Это полезно в смысле того, что все валидации и проверки выполняются на этапе компиляции и определённые типы ошибок, такие как интерпретация типов данных могут быть отловлены на ранней стадии цикла разработки, вместо того чтобы быть выявленными в момент исполнения, что может быть дорогостоящим, в особенности в случае подхода имеющегося блокчейна/ адаптивных контрактов (smart contracts). Прочие свойства данного языка включают в себя наследование, библиотеки и возможность определения составных типов данных.
Solidity также именуется ориентированным под контракты языком. В solidity контракты эквивалентны понятию классов в прочих объектно- ориентированных языках программирования.
Solidity имеет два вида типов данных: определяемых значением типы и ссылочные типы.
Определяемые значением типы
Здесь объясняются подробности определяемых значением типы.
Булевы
Этот тип данных имеет два возможных значения, истина (true
) или ложь
bool v = true;
(false
), например:
Этот оператор присваивает значение true
переменной v
.
Целые числа
Данный тип данных представляет целые. Здесь показана таблица, которая отображает различные ключевые слова, применяемые для объявления целых типов данных.
Ключевое слово | Типы | Подробности |
---|---|---|
|
Целое со знаком |
С |
Целое без знака |
С |
Например, в данном коде, отметим, что 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.
В этом примере функция 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 отображает применение прагмы, импорта и комментариев:
На этом мы завершаем краткое введение в язык программирования solidity. Этот язык очень богат и находится в состоянии постоянного улучшения. Подробная документация и руководства по программированию можно получит в Интернете.
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, например:
Можно развернуть простой контракт с помощью 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
, которую мы уже открывали ранее и осуществим следующие шаги:
-
Определим некую переменную с именем
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
является файлом вывода, произведённым после удаления всех сивмволов новой строки из файла на входе. Данный код затем может быть скопирован и помещён из файла в нашу консоль JavaScriptgeth
. -
Теперь убедимся что необходимый нам компилятор solidity доступен и, если он не доступен, тогда отсылаем вас к тому разделу этой главы, в которой объяснялась установка solidity.
> eth.getCompilers() ["Solidity"]
-
Создайте переменную, назначьте ей код и откомпилируйте его с применением solidity:
> var simplecontractcompiled=eth.compile.solidity(simplecontractsource) undefined
-
Введите
simplecontractcompiled
; это отобразит вывод аналогичный показываемому ниже, так какsimplecontractcompiled
были назначены данные с предыдущего шага 3.
-
Создайте переменную для взаимодействия с имеющимся контрактом:
> var simplecontractinteractor=eth.contract (simplecontractcompiled.valueChecker.info.abiDefinition); undefined
-
Проверьте 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" }]
-
Убедитесь что код
valueChecker
представлен в шестнадцатеричном виде:> simplecontractcompiled.valueChecker.code
Это отобразит следующий вывод. У вас он может слегка отличаться:
"0x6060604052600a60005534610000575b60878061001c6000396000f36060604 05260e060020a6000350463f9d55e218114601c575b6000565b3460005760296004 35603d565b604080519115158252519081900360200190f35b6000805460ff83161 0608157604080516001815290517f3eb1a229ff7995457774a4bd31ef7b13b6f449 1ad1ebb8961af120b8b4b6239c9181900360200190a15060015b5b91905056"
-
Теперь введите следующую порцию кода; отметим, что само поле данных содержит приведённый выше код для
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
-
После того, как требуемая учётная запись успешно разблокирована, запустите майнинг с тем, чтобы данный контракт мог подвергаться добыче (для запуска майнинга нет необходимости в разблокировании. Разблокирование учётной записи необходимо для добычи данного контракта и его создания в имеющемся блокчейне):
> miner.start() true
После того, как данный контракт успешно создан, он отобразит некий вывод, подобный такому:
> [object Object] undefined undefined > [object Object] 0x94a1107f2585f0ab931c71f2f8f02e9f5ab888c0
Он демонстрирует сам адрес только что созданного контракта после добычи этого контракта.
-
Чтобы сделать взаимодействие с этим контрактом проще, значение адреса его учётной записи может быть присвоено некоторой переменной:
> var simplecontractaddress="0x94a1107f2585f0ab931c71f2f8f02e9f5ab888c0" Undefined
-
Теперь имеется множество выставленных методов и этот контракт можно теперь опрашивать далее, например:
> var deployedaddress=eth.getCode(simplecontractaddress); undefined > deployedaddress "0x606060405260e060020a6000350463f9d55e218114601c575b6000565b346000 576029600435603d565b604080519115158252519081900360200190f35b6000805 460ff831610608157604080516001815290517f3eb1a229ff7995457774a4bd31ef 7b13b6f4491ad1ebb8961af120b8b4b6239c9181900360200190a15060015b5b919 05056" > eth.getBalance(simplecontractaddress) 0
-
После этого можно создать некий объект с названием
simplecontractinstance
, который будет применяться для вызова методов:simplecontractinstance = web3.eth.contract(simplecontractcompiled.valueChecker.info.abiDefinition).at(simplecontractaddress);
-
Имеется множество методов, которые теперь можно выставлять и их перечень может выглядеть следующим образом:
> 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
-
Этот контракт можно опрашивать и далее, как это демонстрируется тут. В нашем следующем примере вызывается функция
Matcher
с заданными аргументами. Помните, что в её коде имеется некое условие, которое выполняет проверку того, что если данное значение равно или превышает10
, тогда функция возвращаетtrue
; в противном случае она возвращаетfalse
. Это может выглядеть следующим образом:> simplecontractinstance.Matcher.call(12) true > simplecontractinstance.Matcher.call(9) false > simplecontractinstance.Matcher.call(0) false > simplecontractinstance.Matcher.call(12) true
Можно взаимодействовать с 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
.
Желательно взаимодействовать с контрактами в дружественной пользователю манере через какую- то веб страницу. Имеется возможность взаимодействовать
с такими контрактами при помощи библиотеки web3.js с веб страниц на основе HTML/JS/CSS. Такое содержимое HTML можно обслуживать с применением
любого веб сервера HTTP, в то время как web3.js может подключаться через локальный RPC
с исполняемым клиентом Эфириум (geth
) и предоставлять некий интерфейс для наших контрактов в имеющемся
блокчейне. Такую архитектуру можно отобразить на следующей схеме:
Если 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. Этого можно достичь, выполнив такие шаги:
-
Вначале создайте каталог с названием
/simplecontract/app
в своём домашнем каталоге. -
Затем создайте
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 в процессе развёртывания самого контракта. -
Создайте файл с названием
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 или иной любой веб
контейнер.
Теперь можно применять любой браузер для просмотра нашей веб страницы, обслуживаемой по TCP
port 7777
. Это отображено на следующем примере. Следует отметить, что наш вывод отображён здесь в
окне консоли браузера. для просмотра данного вывода должна быть доступна консоль с самим браузером.
Так как значения вставлены в сам код для упрощения, в 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
Это потребует некоторого времени; после того как он установится, можно применить для отображения подсказки и проверки того, что он установлен верно:
В качестве альтернативы доступен репозиторий по ссылке 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
Она открывает показанный здесь интерфейс командной строки:
После того, как эта консоль становится доступной, можно исполнять различные методы чтобы опрашивать данный контракт. Перечень команд может быть отображён путём набора следующей команды и завершающего символа табуляции:
Для взаимодействия с этим контрактом можно вызывать и прочие методы; например, чтобы получить собственно адрес данного контракта, в представленной консоли 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.
Проостая проверка может быть выполнена следующим образом. Эта проверка исполнит только два тестирования. В её первоначальном файле имеются три теста, однако с целью упрощения здесь применяются только два. Кроме того, данный тест может завершиться неудачей в большинстве имеющихся систем; таким образом, он был удалён из рассматриваемого файла для упрощения. Отказывающий вариант тестирования будет обсуждён позднее. К тому же, следует заметить, что необходимо выполнять майнинг для исполнения данной проверки.
Эти два теста основаны на том файле, который производится инициализацией 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
.
В предыдущем варианте проверки, когда представлен данный контракт, он должен иметь внутри баланс 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. Когда этот тест выполняется, отображается следующий вывод, который показывает, что проверки потерпели неудачу. Это изменение сделано исключительно для целей демонстрации того, чтобы вы могли видеть, что проверки могут терпеть неудачу, и если это происходит, то какой тип вывода будет воспроизведён.
Команда 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 очень распространённый порт для веб приложений.
После того как данный сервер truffle поднят и исполняется с соотвествующим значением порта, наше содержимое может рассматриваться в браузере
и указываться URL http://localhost:7777
.
Другой пример
Здесь демонстрируется пример, в котором создаётся некий простой контракт в solidity и выполняются его миграция и проверка. Этот контракт очень прсотой и выполняет только сложение:
-
Созайте некий католг с названием
simpleTest
:$ mkdir simpleTest
-
Инициализируйте truffle:
$ truffle init
-
Удалите файлы из каталогов. это необходимо чтобы удалить все созданные truffle по умолчанию файлы проекта MetaCoin.
rm -r test/* contracts/* migrations/*
-
Поместите два файла
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); } }
-
Поместите файл
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"); }); }); }); });
-
В своей папке
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(); };
-
Когда все файлы окажутся на своих местах, скомпилируйте все контракты при помощи компилятора truffle. Опционально можете применить флаг
--compile-all
чтобы повторно скомпилировать имеющиеся контракты, даже если они уже были откомпилированы. Он необходим только в случае, если все контракты нуждаются в повторной компиляции:~/simpleTest$ truffle compile Compiling Addition.sol... Compiling Migrations.sol... Writing artifacts to ./build/contracts
-
Выполните миграцию в тестовую сетевую среду Эфириум при помощи миграции 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
Данная команда вначале развернёт сам контракт в сетевую среду Эфириум (в данном примере Частная сеть).
-
Для взаимодействия с данным контрактом можно применять все показанные далее методы. Так как данный контракт 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 чтобы проверить что он работает правильно. Некоторые примеры показаны тут.
После того как исходный код набран и выполнена проверка его синтаксиса, с правой стороны панели будет показан экран, аналогичный отображаемому ниже.
Данный код может быть улучшен множеством способов. Например, также может сохраняться значение даты в некотором соответствии с имеющимся хэшем документа и она может возвращаться при запросе. Код может быть расширен добавлением структур и дополнительной информации, связанной с данным патентом, однако это пример предназначен быть простым и ясным в его понимании; таким образом, мы избегали излишней сложности. Дальнейшие расширения данного кода предоставляются вам в качестве некоторого упражнения.
После клика по Create будут выставлены две функции из данного контракта, как это отображено на следующем снимке экрана:
Функции можно вызывать как это показано в следующем примере:
Аналогично можно вызвать функцию isAlreadyHashed
.
Если та же самая строка передаётся в данную функцию снова, она не будет сохранена, как это демонстрирует следующий снимок экрана:
Кроме того отметим, что имеющееся событие возвращает 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 модно изобразить как это показано в следующей схеме:
Структура скелета некоторого контракта 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 может быть установлен путём следующего процесса:
-
Выгрузка пакета IPFS применяет следующую команду:
$ curl https://dist.ipfs.io/go-ipfs/v0.4.4/go-ipfs_v0.4.4_linux-amd64.tar.gz -O
-
Раскрутите это файл
gz
:$ tar xvfz go-ipfs_v0.4.4_linux-amd64.tar.gz
-
переместите полученный файл
ipfs
в соответствующую папку чтобы сделать её доступной в установленном пути:$ mv go-ipfs/ipfs /usr/local/bin/ipfs
-
Установите необходимый узел 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
-
Введите следующую команду, чтобы гарантировать что
IPFS
был успешно установлен:
-
Запустите необходимый демон 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
-
Скопируйте файлы в 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
-
Теперь к нему можно получить доступ в браузере следующим образом:
Замечание Отметим, что даннй URL указывает на саму файловую систему IPFS.
-
Наконец, чтобы сделать все изменения неизменными, необходимо применить следующую команду:
/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. Кроме этого были представлены подробные пошаговые примеры
разработки адаптивных контрактов и развёртывания. Кроме того обсуждались инфраструктуры развёртывания с практическими примерами с тем,
чтобы вы получили опыт жизненного цикла разработки определённого адаптивного контракта в имеющемся блокчейне Эфириум. Это длинная глава и,
если тесно следовать за упражнениями, вы получите глубокое понимание разработки контрактов, их проверки и развёртывания в Эфириум. Наконец,
обсуждались различные понятия и инструменты, относящиеся к децентрализованному хранению, децентрализованному взаимодействию и Оракулам.
Так как Эвириум и связанные с ним технологии и инфраструктуры находятся в постоянном и быстром развитии, представляется,что более продвинутые
инструменты и техники появятся со временем; однако обсуждаемые в данной главе основы, скорее всего, останутся прежними. Кроме того,
нет возможности обсуждать в этой главе все без исключения инструменты и инфраструктуры доступные в Эфириум, однако все обсуждаюемые инструменты
и техники в применении основного потока и должны представить солидную основу для вас чтобы перейти к последующему уровню. Имеются некоторые
темы, которые специально не обсуждались в данной главе, такие как безопасность адаптивных контрактов, формальная проверка адаптивных
контрактов, блокчеёны как службы в облаке и определённые варианты применения адаптивных контрактов для различных отраслей. Все эти понятия
будут обсуждаться в последующих главах. Я надеюсь, что вы получили удовольствие при чтении данной главы настолько же, насколько мне понравилось
её писать.