New Hyperledger Fabric: Go SDK Software Model



In a previous article, we talked about the new programming model of the codecode in Hyperledger Fabric. Although the release of the new Go SDK has not yet taken place, nothing prevents us now from seeing what awaits us in the future, and even testing it on a test build. Not everything is still implemented (such as commit handlers ), but the planned design of the software model is already clear, so let's take a closer look at it.

In this repository, I slightly modified the sample grid so that it starts with the chain code that we saw in the previous article and added a simple client written using the new API. The repository has simple instructions for starting and configuring the network.

As you can see in go.mod , to use the new API we take not a release (there is no release yet), but all changes to a specific commit (this is a temporary measure before the release of the release): In our application, we import the gateway package in app.go. The essence of the new API in providing common abstractions for all SDKs (go, node, java) and reducing the boilerplate transferred by the developer from project to project. One such abstraction is Wallet.

require github.com/hyperledger/fabric-sdk-go v1.0.0-beta1.0.20200404205634-0e550fc1a918



"github.com/hyperledger/fabric-sdk-go/pkg/gateway"

. The initialization of wallet looks like this:

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 is an implementation of the WalletStore interface:

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
}

As you can see, this interface describes the basic operations with the user identity repository . In addition to the file system, identity can be stored in memory, just use another built-in factory - NewInMemoryWallet () .

Connect to the network:

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 () returns a pointer to Gateway , an object that provides network connectivity. gateway.WithCommitHandler - a functional option for setting a strategy for waiting for commit events. So far, no strategy has been implemented (which means that the default strategy of the "old" SDK "wait for commits from all endorsers" will be used ), but the following is planned:

  • gateway.DefaultCommitHandlers.OrgAll - wait for a commit at all peers of the organization (by default)
  • gateway.DefaultCommitHandlers.OrgAny - waiting for a commit at any organization peer
  • gateway.DefaultCommitHandlers.NetworkAll - waiting for a commit at all peers of all organizations in the channel
  • gateway.DefaultCommitHandlers.NetworkAny - waiting for a commit at any peer in the channel

You can not even wait at all for any commits, more on that later, but first pay attention to this:

network, err := gw.GetNetwork("mychannel")
if err != nil {
	panic(err)
}

contract := network.GetContract("mycc")

First, we get from the channel a subset of peers with which we will interact, and then a pointer to Contract, whose methods will allow us to interact with the chaincode.

Now we have the opportunity to determine whether or not to use some kind of strategy for waiting for a commit. First, let's look at a transaction with a commit waiting:

result, err := contract.SubmitTransaction("Create", "b", "50")


Here we start the simulation of the transaction at the peer, get the answer, send the proposal response to the orderer, wait for the validation and commit in accordance with the above strategy for waiting for the commit. Standard and familiar behavior. We look further.

result, err := contract.EvaluateTransaction("Read", "b")
	if err != nil {
		panic(err)
	}
fmt.Println(string(result))

In this case, the client receives a proposal response and does nothing else. We do not expect any commits . This should be used to read the state.

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 gives a little more control over the transaction, namely the ability to pass functional options:

  • gateway.WithTransient (transient) - to send private data
  • gateway.WithEndorsingPeers ("peer0.org1.example.com") - for manually determining targets


Try calling app.go , substituting various keys, values, and options into transactional methods to see how this works.

This concludes the review of the new API. By the way, it’s quite easy to implement your own wallet, commit handlers, identity, so you can add standard implementations to those that suit your goals, or offer your own ideas and code to the community.

Thank you for the attention.

All Articles