В прошлой статье мы рассмотрели разработанное приложение на Ethereum. Но обошли стороной вопрос как происходит процесс разработки. Ясно, что это не просто написание кода, который сразу работает. Большую часть времени придется потратить на то, чтобы доводить код, который “почти готов”, до рабочего состояния. Деплой, тестирование, отладка — все это в той или иной мере уже затрагивалось здесь например в этих неплохих статьях: раз, два, три (список не полный). В этой статье мы дадим небольшой обзор и возможно в чем-то повторимся, но постараемся сфокусироваться на тех моментах, которые нам показались важными или недосказанными. Плюс за последнее время некоторые штуки изменились, и огромное количество инструкций оказалось устаревшим. Постараемся немного подправить ситуацию.
Для тестирования и деплоя смарт контрактов мы пользуемся Truffle, он скрывает часть низкоуровневой работы за абстракциями, что очень удобно. Описанная версия — 4.0.6. Это не единственный фреймворк, есть еще Embark и Dapple, но по ним ничего сказать не можем, не приходилось работать. Для инициализации проекта надо выполнить команду (выполнится в текущей папке, поэтому предварительно создайте папку проекта и перейдите в нее):
Создается только базовая структура из папок
Вы можете вручную создавать файлы контрактов, миграций и тестов, но в truffle есть и специальные команды:
Можете проверить, что у вас создались соответствующие файлы. Но содержимое совсем базовое, поэтому пока в этой фиче нет особого преимущества по сравнению с созданием тех же файлов вручную.
В более ранних версиях инициализация
получите ту структуру проекта, которая в старых версиях создавалась по умолчанию после
Увидите подобный текст:
Что делает эта команда? Она поднимает тестовое окружение и дает доступ к нему через консоль. Тестовое окружение — это то, что вы могли видеть в более старых руководствах под названием TestRPC. На самом деле это оно и есть, просто команда Truffle взяла его под свое управление и переименовала в Ganache. Но об этом напишем далее, а пока перейдем к консоли. Выполним полный цикл команд для компиляции, миграции и тестирования:
Можно повызывать методы задеплоенных контрактов вручную, например так:
Как видите все выполняется мгновенно и можно отследить изменения: отправили 3000 с основного адреса на другой, видим, что баланс уменьшился. Выходить из консоли командой:
Truffle development это режим с собственной тестовой нодой. Для подключения к реальной сети или тестнету используется команда
Есть еще и GUI версия. Принципиального отличия нет, но в этой версии можно сразу видеть всю информацию, события и балансы. Может быть очень удобно в некоторых ситуациях. Можете использовать любую из этих версий
Но если вы просто сделали
Поэтому добавим в
Вы можете увидеть еще и
Этот конфиг даст возможность трюфелю подключаться к любой сети, доступной на localhost:8545. Это значения по умолчанию для geth и ganache-cli. Если вы используете GUI-версию Ganache, зайдите в настройки и при необходимости измените порт и перезапустите (кнопка “save and restart”).
Если застряли на окне с логотипом, то скорее всего у вас на этом же порту уже что-то запущено, geth, ganache-cli/testrpc или что-то еще
Теперь можно подключиться, выполняем команду:
И можно попробовать например сделать:
В GUI-версии вы сразу сможете увидеть изменение баланса аккаунта, с которого прошел деплой, если зайдете во вкладку blocks — то увидите смайненные блоки и сколько газа расходовалось на транзакции в них. Щелкнув по каждому из них можно получить еще более подробную информацию. Вкладка transactions таким же образом покажет вам все прошедшие транзакции.
В консольной версии будет та же информация, но в виде стены логов:
Используйте что считаете удобнее.
Кстати если вы читали другие инструкции по этой же теме, то наверное уже знаете, что не обязательно вызывать консоль, чтобы компилировать и деплоить, можно просто вызвать например
И это будет полным аналогом того, что мы сделали выше. Такой же принцип и для всех других команд трюфеля. Полный список команд здесь.
Ни в каком языке вы не обойдетесь лишь core-функциональностью. Изобретать велосипеды непродуктивно и опасно, а со смарт контрактами, где вы рискуете чужими деньгами, это особенно критично. Поэтому возникает вопрос где взять эту уже проверенную дополнительную функциональность. В Truffle для этого есть система пакетов, которые доступны в двух вариантах установки (не считая обычного копипаста): с помощью npm и с помощью ethpm.
До этого на примере Metacoin мы видели простейший токен. Токены используются довольно часто, даже Crypto Kitties — это по сути токены, хоть и оригинального стандарта. Основной стандарт токенов сейчас — это ERC20. Чтобы соответствовать стандарту токен должен реализовывать набор функций, которые могут обеспечивать универсальное и безопасное использование в кошельках, биржах и т.д. Тут очень полезен оказывается пакет zeppelin-solidity от OpenZeppelin — набор библиотек для часто используемых паттернов в смарт контрактах. В этой статье например уже было описано использование этого пакета. Рассмотрим не использование, а способы установки и подключения. Для начала тот, который описан в инструкции на гитхабе проекта. В корне проекта truffle выполните:
После чего в папке
Но существует еще один способ установки пакетов, который разработан специально для пакетов в Ethereum: EthPM. Пакеты хранятся на IPFS. Список пакетов доступен по ссылке. Он интегрирован в трюфель и чтобы установить тот же самый zeppelin-solidity можно выполнить:
Добавится папка
Но если вы сравните версии, которые установлены тем и другим способом, то обнаружите, что они разные. И по крайней в данный момент версия npm новее (на момент написания 1.6.0 против 1.3.0 в EthPm). Так что хоть идеологически EthPM и интереснее, но пока наверное лучше устанавливать пакеты с помощью npm
Обычно когда при выполнении смарт контракта происходит ошибка, сообщения оказываются очень неинформативными. Для примера сделаем контракт с намеренной ошибкой:
В файле contracts/FaultyContract.sol добавьте недостающий код:
Как видите здесь неизбежно деление на 0.
В файле
Откроем develop консоль, скомплируем и задеплоем наш контракт
Получим контракт и вызовем проблемную функцию:
Как видите не, очень информативно, неизвестно в каком месте ошибка и что значит
Эта команда позволяет видеть логи того, что происходит в основной develop-консоли, и в том числе там можно найти хеш транзакций. Запустим функцию еще раз
В окне с логом будет что-то похожее на
Возьмем хеш транзакции и передадим его в команду debug:
Вам должны вывестись подсказки дальнейших команд. Чтобы понять хотя бы в какой строчке ошибка, можно использовать команду
Каждый шаг будет отображаться несколько строк из кода и подчеркиваться часть, которая в данный момент исполняется. Есть возможность перейти на совсем низкий уровень и по-очереди выполнять каждый opcode (команды виртуальной машины Ethereum) и смотреть состояние стека. Сделаем еще один шаг и попробуем вывести состояние стека:
Видим, что последними в стеке лежат наши x и y, 16 и 4. Это конечно не сильно удобный способ и нужно разбираться с опкодами и как они выполняются в виртуальной машине Ethereum. Если интересно — можете глянуть например yellow paper (H.2. Instruction Set). Но у нас задача просто найти строчку с ошибкой. Продолжим выполнять
Тут хотя бы можно увидеть, что ошибка произошла где-то в части деления. И можно смотреть на стек и инструкции, если разбираетесь. Это к сожалению все, что предлагает трюфель по части дебага. Ну, хоть что-то.
В этом плане для одиночных контрактов и несложных связей можем посоветовать Remix IDE (в этой статье например автор пользуется ей для деплоя), там есть практически полноценный дебаг с возможностью видеть значения переменных на каждом шаге. Интерфейс интуитивный, смотрите как уже рассмотренный пример можно будет отладить там:
В консоли внизу видим ошибку, нажимаем дебаг:
И можем прокручивать туда-сюда и видеть значения переменных в человеческом виде.
В прошлой статье мы рассматривали плагин Metamask, который позволяет подключаться к блокчейну без использования локального синхронизированного Ethereum-клиента. Это возможно благодаря сервису Infura. Вы тоже можете получить доступ к нодам Infura и подключаться к ним через truffle. Для этого во-первых нужно зарегистрироваться на их сайте, в письме вам придут ссылки с персональными токенами для доступа. Давайте попробуем задеплоить пример Metacoin на Ropsten без локальной ноды.
Создаем тестовый проект как обычно:
Далее нам понадобится дополнительный пакет HDWalletProvider, с помощью которого Truffle может подписывать транзакции
Добавим провайдер в настройки трюфеля таким кодом:
Пришлось задать gas и gasPrice, потому что по крайней мере у нас значения по умолчанию не подошли. Не забудьте вставить токен из письма, а еще придумайте свою мнемонику из 12 разных (а не как в примере) слов — она используется для генерации аккаунтов и если кто-то ей завладеет, то сможет сгенерировать те же самые аккаунты и воспользоваться ими без вашего ведома. Например если вы попробуете использовать мнемонику в этой статье, то сможете воспользоваться тем эфиром (0.3), который мы туда переслали (если его не израсходует кто-то еще). В этом конфиге также оставлен
Перед тем как вызывать
Перешлите на ваш аккаунт немного Ropsten-эфира для того, чтобы можно было заплатить за деплой. После того, как он дошел (можно проверять предыдущей командой), можно попробовать выполнить
Результат должен быть таким:
Видим хеш транзакций, можно проверить, что они действительно попали на Ropsten через etherscan.io (например ropsten.etherscan.io/tx/0x93cf7dbde8c362534dc912926fc4d7df54c9c1f5e0a7dcfd964a0177b42bc7be)
Надеемся вы узнали что-то новое из этой статьи или хотя бы освежили знания.
Что касается следующей статьи, то как показала практика, сложно сделать реальный полезный проект без связи с внешним миром через Oraclize и IPFS. Об этом и планируем написать.
Погружение в разработку на Ethereum:
Часть 1: введение
Часть 2: Web3.js и газ
Часть 3: приложение для пользователя
Проект в truffle
Для тестирования и деплоя смарт контрактов мы пользуемся Truffle, он скрывает часть низкоуровневой работы за абстракциями, что очень удобно. Описанная версия — 4.0.6. Это не единственный фреймворк, есть еще Embark и Dapple, но по ним ничего сказать не можем, не приходилось работать. Для инициализации проекта надо выполнить команду (выполнится в текущей папке, поэтому предварительно создайте папку проекта и перейдите в нее):
$ truffle init
Создается только базовая структура из папок
contracts
, migrations
и tests
. В contracts
и migrations
вы можете увидеть смарт контракт Migrations
, который отвечает за логику деплоя (в терминах трюфеля — миграции). А логика примерно такая: в папке migrations
вы складываете скрипты и называете их по шаблону 1_description.js
, 2_another_one.js
… n_etc.js
. Самое важное в названии — это индекс, который идет в начале, после этого можно добавлять любое описание, которое нужно только для читабельности. Индекс же используется для выполнения миграций в порядке нумерации. Смарт контракт Migrations
используется для того, чтобы сохранять какие из скриптов миграций уже выполнились. Так что если в процессе разработки добавлять новые контракты и новую логику деплоя, то предыдущий успешный прогресс передеплоивать не надо. Лично мы этим не пользуемся, вместо этого имеем фиксированное количество миграций, редактируем и запускаем их каждый раз заново с помощью truffle migration --reset
.Вы можете вручную создавать файлы контрактов, миграций и тестов, но в truffle есть и специальные команды:
$ truffle create contract ExampleContract
$ truffle create migration ExampleMigration
$ truffle create test ExampleContract
Можете проверить, что у вас создались соответствующие файлы. Но содержимое совсем базовое, поэтому пока в этой фиче нет особого преимущества по сравнению с созданием тех же файлов вручную.
Работа с проектом
В более ранних версиях инициализация
truffle init
создавала сразу небольшой пример (Metacoin и ConvertLib). Чтобы в последних версиях увидеть этот и другие примеры, можно воспользоваться фичей, называемой Truffle Boxes. Боксы созданы для того, чтобы получить полноценный пример проекта Truffle и его взаимодействия с разными web-средствами, например ReactJS. Здесь перечислен список боксов, как официальных, так и созданных сообществом. Создайте директорию для нового проекта и перейдите в нее. Затем выполним команду:$ truffle unbox metacoin
получите ту структуру проекта, которая в старых версиях создавалась по умолчанию после
truffle init
. Это пример с базовым токеном Metacoin, который можно пересылать от пользователя к пользователю и с помощью библиотеки ConvertLib смотреть баланс в эфире при фиксированном курсе обмена. Кроме того, что тут показано как создавать и использовать смарт контракт и библиотеку, здесь есть еще и пример тестов на JavaScript и Solidity (подробнее о написании тестов можете почитать здесь, обратите внимание, что там как раз рассматривается более старая версия Truffle). Давайте бегло рассмотрим как можно в тестовом режиме собрать и проверить этот проект. Для начала запустим development консоль:$ truffle develop
Увидите подобный текст:
Truffle Develop started at http://localhost:9545/
Accounts:
(0) 0x627306090abab3a6e1400e9345bc60c78a8bef57
(1) 0xf17f52151ebef6c7334fad080c5704d77216b732
(2) 0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef
(3) 0x821aea9a577a9b44299b9c15c88cf3087f3b5544
(4) 0x0d1d4e623d10f9fba5db95830f7d3839406c6af2
(5) 0x2932b7a2355d6fecc4b5c0b6bd44cc31df247a2e
(6) 0x2191ef87e392377ec08e7c08eb105ef5448eced5
(7) 0x0f4f2ac550a1b4e2280d04c21cea7ebd822934b5
(8) 0x6330a553fc93768f612722bb8c2ec78ac90b3bbc
(9) 0x5aeda56215b167893e80b4fe645ba6d5bab767de
Mnemonic: candy maple cake sugar pudding cream honey rich smooth crumble sweet treat
truffle(develop)>
Что делает эта команда? Она поднимает тестовое окружение и дает доступ к нему через консоль. Тестовое окружение — это то, что вы могли видеть в более старых руководствах под названием TestRPC. На самом деле это оно и есть, просто команда Truffle взяла его под свое управление и переименовала в Ganache. Но об этом напишем далее, а пока перейдем к консоли. Выполним полный цикл команд для компиляции, миграции и тестирования:
truffle(develop)> compile
Результат
Compiling ./contracts/ConvertLib.sol...
Compiling ./contracts/MetaCoin.sol...
Compiling ./contracts/Migrations.sol...
Writing artifacts to ./build/contracts
truffle(develop)> migrate
Результат
Using network 'develop'.
Running migration: 1_initial_migration.js
Deploying Migrations...
... 0x29619f8ba9b9e001bef885c8ca2fbee45beab738adc41c7f9e2e8273fbc67e9f
Migrations: 0x8cdaf0cd259887258bc13a92c0a6da92698644c0
Saving successful migration to network...
... 0xd7bc86d31bee32fa3988f1c1eabce403a1b5d570340a3a9cdba53a472ee8c956
Saving artifacts...
Running migration: 2_deploy_contracts.js
Deploying ConvertLib...
... 0x02318651545ac96670af626ef7795cb928d7504afc3f856258058ce579d47fe6
ConvertLib: 0x345ca3e014aaf5dca488057592ee47305d9b3e10
Linking ConvertLib to MetaCoin
Deploying MetaCoin...
... 0x486c572bbb2df30bb166f5507423d394807b5b92041860968a7d5eb162e42e48
MetaCoin: 0xf25186b5081ff5ce73482ad761db0eb0d25abfbf
Saving successful migration to network...
... 0x059cf1bbc372b9348ce487de910358801bbbd1c89182853439bec0afaee6c7db
Saving artifacts...
truffle(develop)> test
Результат
Using network 'develop'.
Compiling ./contracts/ConvertLib.sol...
Compiling ./contracts/MetaCoin.sol...
Compiling ./test/TestMetacoin.sol...
Compiling truffle/Assert.sol...
Compiling truffle/DeployedAddresses.sol...
TestMetacoin
✓ testInitialBalanceUsingDeployedContract (62ms)
✓ testInitialBalanceWithNewMetaCoin (47ms)
Contract: MetaCoin
✓ should put 10000 MetaCoin in the first account
✓ should call a function that depends on a linked library (54ms)
✓ should send coin correctly (117ms)
5 passing (796ms)
Можно повызывать методы задеплоенных контрактов вручную, например так:
truffle(develop)> var metaCoin
truffle(develop)> MetaCoin.deployed().then( function(instance) { metaCoin = instance } );
truffle(develop)> metaCoin.getBalance(web3.eth.coinbase)
BigNumber { s: 1, e: 4, c: [ 10000 ] }
truffle(develop)> _.toNumber()
10000
truffle(develop)> metaCoin.sendCoin( web3.eth.accounts[2], 3000 )
{ tx: '0x9f59085a9f22c0bd691b890370bcffd7eedce1327a3bb525a2de3edf9db0d279',
receipt:
{ transactionHash: '0x9f59085a9f22c0bd691b890370bcffd7eedce1327a3bb525a2de3edf9db0d279',
transactionIndex: 0,
blockHash: '0x24e8913b6f707bb5e5acbaa054fef9dabd548a561dc988763209f0aeed9a57b5',
blockNumber: 12,
gasUsed: 51024,
cumulativeGasUsed: 51024,
contractAddress: null,
logs: [ [Object] ] },
logs:
[ { logIndex: 0,
transactionIndex: 0,
transactionHash: '0x9f59085a9f22c0bd691b890370bcffd7eedce1327a3bb525a2de3edf9db0d279',
blockHash: '0x24e8913b6f707bb5e5acbaa054fef9dabd548a561dc988763209f0aeed9a57b5',
blockNumber: 12,
address: '0xf25186b5081ff5ce73482ad761db0eb0d25abfbf',
type: 'mined',
event: 'Transfer',
args: [Object] } ] }
truffle(develop)> metaCoin.getBalance(web3.eth.coinbase)
BigNumber { s: 1, e: 3, c: [ 7000 ] }
truffle(develop)> _.toNumber()
7000
Как видите все выполняется мгновенно и можно отследить изменения: отправили 3000 с основного адреса на другой, видим, что баланс уменьшился. Выходить из консоли командой:
truffle(develop)> .exit
Подключение к ноде
Truffle development это режим с собственной тестовой нодой. Для подключения к реальной сети или тестнету используется команда
truffle console
, которая полностью аналогична develop
, но не поднимает тестовое окружение. Чтобы продемонстрировать это, не обязательно запускать geth
, можно воспользоваться например уже упомянутой Ganache, заменившей собой TestRPC. Запустим командой:$ ganache-cli
Есть еще и GUI версия. Принципиального отличия нет, но в этой версии можно сразу видеть всю информацию, события и балансы. Может быть очень удобно в некоторых ситуациях. Можете использовать любую из этих версий
Но если вы просто сделали
truffle unbox metacoin
и еще никак не меняли конфигурационные файлы, то подключиться не получится$ truffle console
No network available. Use `truffle develop` or add network to truffle.js config.
Поэтому добавим в
truffle.js
следующее:module.exports = {
networks: {
development: {
host: "localhost",
port: 8545,
network_id: "*"
}
}
};
Вы можете увидеть еще и
truffle-config.js
. Это то же самое, но для Windows. Лишний для вашей системы файл можно удалить.Этот конфиг даст возможность трюфелю подключаться к любой сети, доступной на localhost:8545. Это значения по умолчанию для geth и ganache-cli. Если вы используете GUI-версию Ganache, зайдите в настройки и при необходимости измените порт и перезапустите (кнопка “save and restart”).
Если застряли на окне с логотипом, то скорее всего у вас на этом же порту уже что-то запущено, geth, ganache-cli/testrpc или что-то еще
Теперь можно подключиться, выполняем команду:
$ truffle console
И можно попробовать например сделать:
truffle(development)> migrate --reset
В GUI-версии вы сразу сможете увидеть изменение баланса аккаунта, с которого прошел деплой, если зайдете во вкладку blocks — то увидите смайненные блоки и сколько газа расходовалось на транзакции в них. Щелкнув по каждому из них можно получить еще более подробную информацию. Вкладка transactions таким же образом покажет вам все прошедшие транзакции.
Много скриншотов
В консольной версии будет та же информация, но в виде стены логов:
Развернуть
net_version
eth_accounts
eth_accounts
net_version
net_version
eth_sendTransaction
Transaction: 0x29619f8ba9b9e001bef885c8ca2fbee45beab738adc41c7f9e2e8273fbc67e9f
Contract created: 0x922194d35a507e5905fa4f2c9e7172ee8535272a
Gas usage: 269607
Block Number: 1
Block Time: Mon Feb 05 2018 10:28:17 GMT+0300 (MSK)
eth_newBlockFilter
eth_getFilterChanges
eth_getTransactionReceipt
eth_getCode
eth_uninstallFilter
eth_sendTransaction
Transaction: 0xfafc4352cc1dde57e46954d7ebd3a59232599081a253dd8705847a380ae5b06b
Gas usage: 41981
Block Number: 2
Block Time: Mon Feb 05 2018 10:28:17 GMT+0300 (MSK)
eth_getTransactionReceipt
eth_accounts
net_version
net_version
eth_sendTransaction
Используйте что считаете удобнее.
Кстати если вы читали другие инструкции по этой же теме, то наверное уже знаете, что не обязательно вызывать консоль, чтобы компилировать и деплоить, можно просто вызвать например
truffle migrate --reset
И это будет полным аналогом того, что мы сделали выше. Такой же принцип и для всех других команд трюфеля. Полный список команд здесь.
Пакеты
Ни в каком языке вы не обойдетесь лишь core-функциональностью. Изобретать велосипеды непродуктивно и опасно, а со смарт контрактами, где вы рискуете чужими деньгами, это особенно критично. Поэтому возникает вопрос где взять эту уже проверенную дополнительную функциональность. В Truffle для этого есть система пакетов, которые доступны в двух вариантах установки (не считая обычного копипаста): с помощью npm и с помощью ethpm.
До этого на примере Metacoin мы видели простейший токен. Токены используются довольно часто, даже Crypto Kitties — это по сути токены, хоть и оригинального стандарта. Основной стандарт токенов сейчас — это ERC20. Чтобы соответствовать стандарту токен должен реализовывать набор функций, которые могут обеспечивать универсальное и безопасное использование в кошельках, биржах и т.д. Тут очень полезен оказывается пакет zeppelin-solidity от OpenZeppelin — набор библиотек для часто используемых паттернов в смарт контрактах. В этой статье например уже было описано использование этого пакета. Рассмотрим не использование, а способы установки и подключения. Для начала тот, который описан в инструкции на гитхабе проекта. В корне проекта truffle выполните:
$ npm init -y
$ npm install -E zeppelin-solidity
После чего в папке
node_modules
у вас появится zeppelin-solidity
, из которого в смарт контракте можно подключать требуемые файлы, например ownable, строчкойimport 'zeppelin-solidity/contracts/ownership/Ownable.sol';
Но существует еще один способ установки пакетов, который разработан специально для пакетов в Ethereum: EthPM. Пакеты хранятся на IPFS. Список пакетов доступен по ссылке. Он интегрирован в трюфель и чтобы установить тот же самый zeppelin-solidity можно выполнить:
$ truffle install zeppelin
Добавится папка
installed_contracts
, подключать ее содержимое так же, как и из папки node_modules
:import ‘zeppelin/contracts/ownership/Ownable.sol’;
Но если вы сравните версии, которые установлены тем и другим способом, то обнаружите, что они разные. И по крайней в данный момент версия npm новее (на момент написания 1.6.0 против 1.3.0 в EthPm). Так что хоть идеологически EthPM и интереснее, но пока наверное лучше устанавливать пакеты с помощью npm
Отладка
Обычно когда при выполнении смарт контракта происходит ошибка, сообщения оказываются очень неинформативными. Для примера сделаем контракт с намеренной ошибкой:
$ mkdir FaultyContract && cd FaultyContract
$ truffle init
$ truffle create contract FaultyContract
$ truffle create migration deploy
В файле contracts/FaultyContract.sol добавьте недостающий код:
pragma solidity ^0.4.4;
contract FaultyContract {
int public result;
function divideXbyY( int x, int y ) public {
y -= y;
result = x/y;
}
}
Как видите здесь неизбежно деление на 0.
В файле
migration/xxxx_deploy.js
(xxxx — сгенерированный id, может быть разный) добавьте недостающий код для деплоя:var FaultyContract = artifacts.require("./FaultyContract.sol");
module.exports = function( deployer ) {
deployer.deploy( FaultyContract );
}
Откроем develop консоль, скомплируем и задеплоем наш контракт
$ truffle develop
truffle(develop)> migrate
Получим контракт и вызовем проблемную функцию:
truffle(develop)> var faultyContract;
truffle(develop)> FaultyContract.deployed().then( function(instance) { faultyContract = instance } );
truffle(develop)> faultyContract.divideXbyY(16, 4);
Error: VM Exception while processing transaction: invalid opcode
at Object.InvalidResponse (/usr/lib/node_modules/truffle/build/cli.bundled.js:43303:16)
at /usr/lib/node_modules/truffle/build/cli.bundled.js:331156:36
at /usr/lib/node_modules/truffle/build/cli.bundled.js:314196:9
at XMLHttpRequest.request.onreadystatechange (/usr/lib/node_modules/truffle/build/cli.bundled.js:329855:7)
at XMLHttpRequestEventTarget.dispatchEvent (/usr/lib/node_modules/truffle/build/cli.bundled.js:70159:18)
at XMLHttpRequest._setReadyState (/usr/lib/node_modules/truffle/build/cli.bundled.js:70449:12)
at XMLHttpRequest._onHttpResponseEnd (/usr/lib/node_modules/truffle/build/cli.bundled.js:70604:12)
Как видите не, очень информативно, неизвестно в каком месте ошибка и что значит
invalid opcode
. В Truffle с версии 4 доступна команда debug
(пока бета), позволяющая заново перевыполнить транзакцию построчно. Но для этого нужно получить хеш транзакции, а в ошибке даже его нет. Чтобы увидеть хеш, запустите еще один экземпляр truffle develop
с флагом --log
:$ truffle develop --log
Эта команда позволяет видеть логи того, что происходит в основной develop-консоли, и в том числе там можно найти хеш транзакций. Запустим функцию еще раз
truffle(develop)> faultyContract.divideXbyY(16, 4);
В окне с логом будет что-то похожее на
develop:testrpc eth_sendTransaction +0ms
develop:testrpc +27ms
develop:testrpc Transaction: 0x21073e12e7c8fb785347d7bd5d974d4954379dcace7b53d452c03b39ca007b9e +1ms
develop:testrpc Gas usage: 6721975 +0ms
develop:testrpc Block Number: 6 +0ms
develop:testrpc Block Time: Tue Feb 06 2018 10:32:27 GMT+0300 (MSK) +0ms
develop:testrpc Runtime Error: invalid opcode +0ms
develop:testrpc +0ms
Возьмем хеш транзакции и передадим его в команду debug:
truffle(develop)> debug 0x21073e12e7c8fb785347d7bd5d974d4954379dcace7b53d452c03b39ca007b9e
Вам должны вывестись подсказки дальнейших команд. Чтобы понять хотя бы в какой строчке ошибка, можно использовать команду
n
(step next):debug(develop:0x21073e12...)> n
FaultyContract.sol | 0x345ca3e014aaf5dca488057592ee47305d9b3e10:
8: int public result;
9:
10: function divideXbyY( int x, int y ) public {
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
debug(develop:0x21073e12...)>
Каждый шаг будет отображаться несколько строк из кода и подчеркиваться часть, которая в данный момент исполняется. Есть возможность перейти на совсем низкий уровень и по-очереди выполнять каждый opcode (команды виртуальной машины Ethereum) и смотреть состояние стека. Сделаем еще один шаг и попробуем вывести состояние стека:
debug(develop:0x21073e12...)>
FaultyContract.sol | 0x345ca3e014aaf5dca488057592ee47305d9b3e10:
9:
10: function divideXbyY( int x, int y ) public {
11: y -= y;
^
debug(develop:0x21073e12...)> p
FaultyContract.sol | 0x345ca3e014aaf5dca488057592ee47305d9b3e10:
(55) DUP1
00000000000000000000000000000000000000000000000000000000c6329782
000000000000000000000000000000000000000000000000000000000000009b
0000000000000000000000000000000000000000000000000000000000000010
0000000000000000000000000000000000000000000000000000000000000004 (top)
Видим, что последними в стеке лежат наши x и y, 16 и 4. Это конечно не сильно удобный способ и нужно разбираться с опкодами и как они выполняются в виртуальной машине Ethereum. Если интересно — можете глянуть например yellow paper (H.2. Instruction Set). Но у нас задача просто найти строчку с ошибкой. Продолжим выполнять
next step
, пока не получим что-то подобное:10: function divideXbyY( int x, int y ) public {
11: y -= y;
12: result = x/y;
^^^
debug(develop:0x21073e12...)>
Transaction halted with a RUNTIME ERROR.
This is likely due to an intentional halting expression, like assert(), require() or revert(). It can also be due to out-of-gas exceptions. Please inspect your transaction parameters and contract code to determine the meaning of this error.
truffle(develop)>
Тут хотя бы можно увидеть, что ошибка произошла где-то в части деления. И можно смотреть на стек и инструкции, если разбираетесь. Это к сожалению все, что предлагает трюфель по части дебага. Ну, хоть что-то.
В этом плане для одиночных контрактов и несложных связей можем посоветовать Remix IDE (в этой статье например автор пользуется ей для деплоя), там есть практически полноценный дебаг с возможностью видеть значения переменных на каждом шаге. Интерфейс интуитивный, смотрите как уже рассмотренный пример можно будет отладить там:
В консоли внизу видим ошибку, нажимаем дебаг:
И можем прокручивать туда-сюда и видеть значения переменных в человеческом виде.
Удаленные ноды Infura
В прошлой статье мы рассматривали плагин Metamask, который позволяет подключаться к блокчейну без использования локального синхронизированного Ethereum-клиента. Это возможно благодаря сервису Infura. Вы тоже можете получить доступ к нодам Infura и подключаться к ним через truffle. Для этого во-первых нужно зарегистрироваться на их сайте, в письме вам придут ссылки с персональными токенами для доступа. Давайте попробуем задеплоить пример Metacoin на Ropsten без локальной ноды.
Создаем тестовый проект как обычно:
$ mkdir metacoin && cd metacoin
$ truffle unbox metacoin
Далее нам понадобится дополнительный пакет HDWalletProvider, с помощью которого Truffle может подписывать транзакции
$ npm init
$ npm install
$ npm install truffle-hdwallet-provider
Добавим провайдер в настройки трюфеля таким кодом:
var HDWalletProvider = require("truffle-hdwallet-provider");
var mnemonic = "correct horse battery staple correct horse battery staple correct horse battery staple"
module.exports = {
networks: {
development: {
host: "localhost",
port: 8545,
network_id: "*"
},
ropsten: {
provider: function() {
return new HDWalletProvider(mnemonic, "https://ropsten.infura.io/<ваш токен из письма>")
},
network_id: 3,
gas: 4000000,
gasPrice: 21000000000
}
}
};
Пришлось задать gas и gasPrice, потому что по крайней мере у нас значения по умолчанию не подошли. Не забудьте вставить токен из письма, а еще придумайте свою мнемонику из 12 разных (а не как в примере) слов — она используется для генерации аккаунтов и если кто-то ей завладеет, то сможет сгенерировать те же самые аккаунты и воспользоваться ими без вашего ведома. Например если вы попробуете использовать мнемонику в этой статье, то сможете воспользоваться тем эфиром (0.3), который мы туда переслали (если его не израсходует кто-то еще). В этом конфиге также оставлен
development
, выбирать между этими двумя сетями можно запуская truffle с соответствующим названием после флага --network
: $ truffle console --network ropsten
Перед тем как вызывать
migrate
, требуется пополнить баланс сгенерированного аккаунта. Узнаем адрес и баланс командами:
truffle(ropsten)> web3.eth.getAccounts( function(e,r) { console.log(r[0]); } );
undefined
0x0bb542704819b5e6a28deb2b73245be57ce0e78b
truffle(ropsten)> web3.eth.getBalance('0x0bb542704819b5e6a28deb2b73245be57ce0e78b', function(e, r) { console.log( web3.fromWei( r.toNumber() ) ); })
undefined
0
Перешлите на ваш аккаунт немного Ropsten-эфира для того, чтобы можно было заплатить за деплой. После того, как он дошел (можно проверять предыдущей командой), можно попробовать выполнить
migrate
:truffle(ropsten)> migrate
Результат должен быть таким:
Compiling ./contracts/ConvertLib.sol...
Compiling ./contracts/MetaCoin.sol...
Compiling ./contracts/Migrations.sol...
Writing artifacts to ./build/contracts
Using network 'ropsten'.
Running migration: 1_initial_migration.js
Deploying Migrations...
... 0x93cf7dbde8c362534dc912926fc4d7df54c9c1f5e0a7dcfd964a0177b42bc7be
Migrations: 0x02519d13f61bdcad838d938611e6722c3d1f8034
Saving successful migration to network...
... 0xec501a78cc11c723ab60186167765aa7c422177153cd72a976e66441db2b5b95
Saving artifacts...
Running migration: 2_deploy_contracts.js
Deploying ConvertLib...
... 0xf172d9a9ff9f1fdfdfabc816d89f5a5e710ba26e3a2ad9e1661c9dea56564f04
ConvertLib: 0xd04bffb73bf546985938a596565141d3a3bf7f0d
Linking ConvertLib to MetaCoin
Deploying MetaCoin...
... 0x4d9814f1d9a959e83828bf26319dd91d73be977395d88e9e8239bb4c4ed5b0eb
MetaCoin: 0x20fd16643d857ce544a91ae4c80385af99dad196
Saving successful migration to network...
... 0x0dff866460d24d56d94dcf5f833aa4fa8ae289cb708ff5c9012ce21447575ce8
Saving artifacts...
truffle(ropsten)>
Видим хеш транзакций, можно проверить, что они действительно попали на Ropsten через etherscan.io (например ropsten.etherscan.io/tx/0x93cf7dbde8c362534dc912926fc4d7df54c9c1f5e0a7dcfd964a0177b42bc7be)
Что дальше?
Надеемся вы узнали что-то новое из этой статьи или хотя бы освежили знания.
Что касается следующей статьи, то как показала практика, сложно сделать реальный полезный проект без связи с внешним миром через Oraclize и IPFS. Об этом и планируем написать.
Погружение в разработку на Ethereum:
Часть 1: введение
Часть 2: Web3.js и газ
Часть 3: приложение для пользователя