Дебажим chaincode в Hyperledger Fabric

в 13:59, , рубрики: blockchain, fabric, Go, Hyperledger

image

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

Про сам блокчейн, его особенности и различные реализации в интернете, и на Хабре в частности, можно найти тонну информации. Но чем глубже вопросы, тем меньше на них ответов. В официальной документации fabric очень мало информации про дебаг, а та которая есть заключается в том, что бы просто залогировать весь код и смотреть, что же пошло не так. Русскоязычные разработчики IBM ответили точно так же. Так что в данной статье будет освещена одна из наших бывших проблем, связанная с дебагом чейнкода в одном из проектов Hyperledger, а именно — Fabric (v0.6).

Чейнкод в fabric это ничто иное, как привычный нам смарт-контракт. Чейнкод может быть написан на Go, Java и, в скором будущем, на JavaScript. В статье рассматривается реализация чейнкода на Go, но думаю данный подход подойдёт и для Java-реализаций.

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

  1. Нода получает запрос на деплой → ищет docker-контейнер → если не находит, то создаёт новый docker-контейнер с чейнкодом внутри, если же находит, то использует его
  2. После создания контейнера, в нём запускается исполняющий файл чейнкода, который подключается по gRPC к ноде и начинает слушать её указания
  3. Когда на ноду поступает какой-либо 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 параметра, а именно:

  1. Указать переменную Environment — CORE_CHAINCODE_ID_NAME=mapCC
  2. Указать аргумент запуска -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:UsersvasyaAppDataLocalTempBuild 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-запросам.

На этом статья подходит к концу, руками и ногами просьба сильно не бить, я всего лишь джуниор и это моя первая статься на Хабре и надеюсь она поможет кому-нибудь так как подобной статьи я не нашёл нигде.

Автор: iTichok

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js