Добрый день, дорогие хаброжители и в особенности те, которые в той или иной мере интересуются блокчейном. Так уж получилось, что придя работать java-разработчиком в одну компанию, мне пришлось заниматься проектами, использующими блокчейн.
Про сам блокчейн, его особенности и различные реализации в интернете, и на Хабре в частности, можно найти тонну информации. Но чем глубже вопросы, тем меньше на них ответов. В официальной документации fabric очень мало информации про дебаг, а та которая есть заключается в том, что бы просто залогировать весь код и смотреть, что же пошло не так. Русскоязычные разработчики IBM ответили точно так же. Так что в данной статье будет освещена одна из наших бывших проблем, связанная с дебагом чейнкода в одном из проектов Hyperledger, а именно — Fabric (v0.6).
Чейнкод в fabric это ничто иное, как привычный нам смарт-контракт. Чейнкод может быть написан на Go, Java и, в скором будущем, на JavaScript. В статье рассматривается реализация чейнкода на Go, но думаю данный подход подойдёт и для Java-реализаций.
Немного предисловия о том, как используется чейнкод со стороны ноды:
- Нода получает запрос на деплой > ищет docker-контейнер > если не находит, то создаёт новый docker-контейнер с чейнкодом внутри, если же находит, то использует его
- После создания контейнера, в нём запускается исполняющий файл чейнкода, который подключается по gRPC к ноде и начинает слушать её указания
- Когда на ноду поступает какой-либо Invoke/Query-запрос, она просто посылает подключенным исполняющим файлам команды, они обрабатывают их и отсылают результат обратно
Соответственно, изначально мы искали способ, как обмануть ноду, что бы она не искала контейнер, но успехом это не увенчалось. Быстренькое чтение docker-compose файлов выявило интересный параметр — CORE_CHAINCODE_MODE, с помощью которого можно заставить ноду не создавать и не искать контейнеры, а просто довериться окружающей среде.
Итак, в качестве среды будет использованы:
- 1 нода в качестве membersrvc
- 1 нода в качестве validation peer (далее VP)
- Intellij Idea с плагином для Go
Как поднимать среду расписывать не буду, так как документации по разворачиванию очень много, стоит лишь отметить некоторые особенности:
Так как используется всего 1 VP, то алгоритм консенсуса PBFT, который используется по-умолчанию, нам не подходит. Для этого в конфигурации (docker-compose файле) необходимо добавить параметр
CORE_PEER_VALIDATOR_CONSENSUS_PLUGIN=noops.
Теперь необходимо указать, что наша среда должна быть запущена в dev-моде, для этого служит необходимо установить параметр CORE_CHAINCODE_MODE=dev.
Также необходимо прокинуть порты 7050 (используется для REST API ноды) и 7051 (используется для gRPC подключения к ноде).
На этом необходимые параметры заканчиваются, остальное можете настроить по своему усмотрению.
В качестве примера будет использоваться пример чейнкода из официального репозитория Hyperledger, а именно — map.
Теперь перейдём к настройкам среды. Для этого необходимо установить 2 параметра, а именно:
- Указать переменную Environment — CORE_CHAINCODE_ID_NAME=mapCC
- Указать аргумент запуска -peer.address=ip:gRpcPort
Вместо "mapCC" можно указать любое имя чейнкода. После этого нужно запустить чейнкод через метод main в режиме дебага. IDE должна вывести следующую информацию:
13:36:57.675 [shim] DEBU: Peer address: 192.168.1.1:7051
13:36:57.676 [shim] DEBU: os.Args returns: [C:\Users\vasya\AppData\Local\Temp\Build map.go and rungo -peer.address=192.168.1.1:7051]
13:36:57.701 [shim] DEBU: Registering… sending REGISTER
13:36:57.702 [shim] DEBU: []Received message REGISTERED from shim
13:36:57.702 [shim] DEBU: []Handling ChaincodeMessage of type: REGISTERED(state:created)
13:36:57.702 [shim] DEBU: Received REGISTERED, ready for invocations
Далее необходимо послать команду деплоя на ноду.
curl -X POST --header "Content-Type: application/json" --header "Accept: application/json" -d "{
\"jsonrpc\": \"2.0\",
\"method\": \"deploy\",
\"params\": {
\"type\": 1,
\"chaincodeID\":{
\"name\":\"mapCC\"
},
\"ctorMsg\": {
\"function\":\"init\"
},
\"secureContext\": \"test_user0\"
},
\"id\": 1
}" ip:RESTPort/chaincode
Тут же в IDE появится информация
13:37:12.038 [shim] DEBU: [mapCCId2]Received message INIT from shim
13:37:12.038 [shim] DEBU: [mapCCId2]Handling ChaincodeMessage of type: INIT(state:established)
13:37:12.038 [shim] DEBU: Entered state init
13:37:12.038 [shim] DEBU: [mapCCId2]Received INIT, initializing chaincode
13:37:12.038 [shim] DEBU: [mapCCId2]Init succeeded. Sending COMPLETED
13:37:12.038 [shim] DEBU: [mapCCId2]Move state message COMPLETED
13:37:12.038 [shim] DEBU: [mapCCId2]Handling ChaincodeMessage of type: COMPLETED(state:init)
13:37:12.038 [shim] DEBU: [mapCCId2]send state message COMPLETED
После этого можно ставить breakpoint в любом нужном месте и дебажиться, отправляя Invoke/Query запросы. Думаю о них писать уже не надо, всё практически аналогично Init-запросам.
На этом статья подходит к концу, руками и ногами просьба сильно не бить, я всего лишь джуниор и это моя первая статься на Хабре и надеюсь она поможет кому-нибудь так как подобной статьи я не нашёл нигде.
rraderio
Почему не v1?
iTichok
По словам разработчиков, альфа-версию v1 можно ожидать не раньше апреля, поэтому решение пало на v0.6.