В предыдущей статье мы говорили о новой программной модели чейнкода в Hyperledger Fabric. И хотя релиз нового Go SDK ещё не состоялся, ничто не мешает нам уже сейчас посмотреть, что нас ждет в будущем, и даже протестить это на тестовой сборке. Не всё ещё реализовано (как, например, commit handlers), но планируемый дизайн программной модели уже ясен, так что давайте взглянем на него ближе.
В данном репозитории я немного изменил семпловую сетку, чтобы она запускалась с чейнкодом, который мы видели в предыдущей статье, и добавил простого клиента, написанного с использованием нового API. В репозитории есть простая инструкция по запуску и настройке сети.
Как можно видеть в go.mod, для использования нового API мы берем не релиз (релиза ещё нет), а все изменения до конкретного коммита (это временная мера до выпуска релиза):
require github.com/hyperledger/fabric-sdk-go v1.0.0-beta1.0.20200404205634-0e550fc1a918
В нашем приложении в app.go импортируем пакет gateway
"github.com/hyperledger/fabric-sdk-go/pkg/gateway"
Суть нового API в предоставлении единых для всех SDK (go, node, java) абстракций и уменьшение бойлерплейта, переносимого разработчиком из проекта в проект. Одна из таких абстракций — Wallet. Инициализация wallet'a выглядит так:
wallet, err := gateway.NewFileSystemWallet("wallet")
if err != nil {
fmt.Errorf(err.Error())
}
err = wallet.Put("admin", gateway.NewX509Identity("Org1MSP", string(cert), string(privKey)))
if err != nil {
fmt.Errorf(err.Error())
}
Wallet — это реализация интерфейса WalletStore:
type WalletStore interface {
Put(label string, stream []byte) error
Get(label string) ([]byte, error)
List() ([]string, error)
Exists(label string) bool
Remove(label string) error
}
Как видно, этот интерфейс описывает базовые операции с хранилищем identity пользователей. Кроме файловой системы identity можно хранить в памяти, просто используйте другую встроенную фабрику — NewInMemoryWallet().
Подключаемся к сети:
gw, err := gateway.Connect(gateway.WithConfig(config.FromFile("./../connection-org1.yaml")),
gateway.WithIdentity(wallet, "admin"),
gateway.WithDiscovery(true),
gateway.WithCommitHandler(gateway.DefaultCommitHandlers.OrgAny))
gateway.Connect() возвращает указатель на Gateway — объект, предоставляющий подключение к сети. gateway.WithCommitHandler — функциональная опция для задания стратегии ожидания событий коммита. Пока не реализована никакая стратегия (значит будет использоваться дефолтная стратегия «старого» SDK «ждать коммиты от всех эндорсеров»), но планируется следующее:
- gateway.DefaultCommitHandlers.OrgAll — ожидание коммита на всех пирах организации (по умолчанию)
- gateway.DefaultCommitHandlers.OrgAny — ожидание коммита на любом пире организации
- gateway.DefaultCommitHandlers.NetworkAll — ожидание коммита на всех пирах всех организаций в канале
- gateway.DefaultCommitHandlers.NetworkAny — ожидание коммита на любом пире в канале
Можно даже не ждать вообще никаких коммитов, об этом далее, но сначала обратите внимание на это:
network, err := gw.GetNetwork("mychannel")
if err != nil {
panic(err)
}
contract := network.GetContract("mycc")
Сначала мы получаем из канала подмножество пиров, с которыми будем взаимодействовать, а затем указатель на Contract, методы которого позволят нам взаимодействовать с чейнкодом.
Теперь у нас есть возможность определить, стоит ли вообще использовать какую-то стратегию ожидания коммита. Для начала посмотрим на транзакцию с ожиданием коммита:
result, err := contract.SubmitTransaction("Create", "b", "50")
Здесь мы запускаем симуляцию транзакции на пире, получаем ответ, отправляем proposal response на orderer, дожидаемся валидации и коммита в соответствии с вышеопределенной стратегией ожидания коммита. Стандартное и знакомое поведение. Смотрим далее.
result, err := contract.EvaluateTransaction("Read", "b")
if err != nil {
panic(err)
}
fmt.Println(string(result))
В данном случае клиент получает proposal response и не делает больше ничего. Мы не ждем никаких коммитов. Это следует использовать для чтения состояния.
transient := make(map[string][]byte)
transient["someTransient"] = []byte("mytransientdata")
txn, err := contract.CreateTransaction("Update", gateway.WithTransient(transient))
if err != nil {
panic(err)
}
result, err = txn.Submit("b", "100")
if err != nil {
panic(err)
}
contract.CreateTransaction дает чуть больше контроля над транзакцией, а именно возможность передать функциональные опции:
- gateway.WithTransient(transient) — для отправки приватных данных
- gateway.WithEndorsingPeers(«peer0.org1.example.com») — для ручного определения targets
Попробуйте вызвать app.go, подставляя различные ключи, значения и опции в транзакционные методы, чтобы увидеть наглядно, как это работает.
На этом обзор нового API заканчивается. Кстати, имплементировать собственный wallet, commit handlers, identity достаточно легко, поэтому вы можете дополнить стандартные реализации тем, что соответствует вашим задачам, или предложить сообществу собственные идеи и код.
Спасибо за внимание.