hyperledger fabric链码开发(Chaincode for Developers)

Posted Share_Boy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hyperledger fabric链码开发(Chaincode for Developers)相关的知识,希望对你有一定的参考价值。

What is Chaincode(什么是链码)?

                链码(ChainCode)是一个用GO语言编写的实现了规定接口的程序。事实上,它也支持其他语言,比如JAVA。链码(ChainCode)运行在一个安全的Docker容器中,该容器与其他的程序相隔离。链码(ChainCode)通过应用程序提交的事务来初始化和管理账本状态(the ledger state).

                链码(chainCode)通常处理网络成员同意的业务逻辑,所以它类似于“智能合约”(smart contract)。由链码(ChainCode)创建的分类状态仅限于该链码,不能由其他链码(ChainCode)直接访问。考虑到适当的许可,一个链码(ChainCode)可以调用另一个链码(ChainCode)来访问其在相同网络中的状态。

                 在下面的章节中,我们将以程序开发人员的眼光来探索链码(ChainCode)。我们将简单的介绍一个链码(ChainCode)的应用程序示例,并介绍链码(ChainCode)Shim API的每种方法的用途。                 

Chaincode API(链码API)

                 每一个链码(ChainCode)程序都必须实现链码(ChainCode)接口,其方法被调用以响应接收到的事务。当链码(ChainCode)接收到一个instantiate或者upgrade事务时执行Init方法进行必要的初始化,包括初始化应用程序的状态。调用Invoke方法来处理处理应用程序提交的事务。

                   链码(ChainCode)的shim API中的另一个接口是ChaincodeStubInterface, 用于访问和修改分类账本和进行链码之间的调用。

                   在本教程中,我们将通过实现一个简单的“资产”管理的链码来演示如何使用这些API。

Simple Asset Chaincode(简单资产链码)

                    我们的程序是一个简单的在账本中创建资产(键值对)。

Choosing a Location for the Code(选择代码的位置)

                    如果你还未使用过Go语言进行编码,你也许需要去确定你是否安装了Go编程语言并且你的系统做过正确的配置

                    现在你需要在你的$GOPATH/src/下创建一个子目录去存储你的链码程序。

                    为了是事情简单,我们提供了一句简单的指令:        

mkdir -p $GOPATH/src/sacc && cd $GOPATH/src/sacc

                    我们创建一个源文件:

touch sacc.go

Housekeeping(编码)

                    首先让我们准备编码。像每一个链码一样,它实现了链码的Init和Invoke函数。所以让我们使用go import语句导入链码的必要依赖库,我们将导入链码的shim 包和peer protobuf 包。接下来让我们添加一个结构 SimpleAsset作为链码 shim 函数的接受器。

package main

import (
    "fmt"

    "github.com/hyperledger/fabric/core/chaincode/shim"
    "github.com/hyperledger/fabric/protos/peer"
)

// SimpleAsset implements a simple chaincode to manage an asset
type SimpleAsset struct 


Initializing the Chaincode(初始化链码)

            下一步我们实现Init函数。

// Init is called during chaincode instantiation to initialize any data.
func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response 

            请注意! 链码升级时也会调用Init函数,在编写将升级现有链码的链码时,请适当的修改Init函数。特别是如果没有迁移或者没有任何内容作为升级的一部分进行初始化,请提供一个空的Init方法。

            接下来我们将使用ChaincodeStubInterface.GetStringArgs函数检索Init调用的参数并检查其有效性,我们的例子中应当接受到的是一个键值对。

// Init is called during chaincode instantiation to initialize any
// data. Note that chaincode upgrade also calls this function to reset
// or to migrate data, so be careful to avoid a scenario where you
// inadvertently clobber your ledger's data!
func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response 
  // Get the args from the transaction proposal
  args := stub.GetStringArgs()
  if len(args) != 2 
    return shim.Error("Incorrect arguments. Expecting a key and a value")
  

            下一步我们确定调用合法,我们将存储初始化状态到账本中。要做到这一点我们将调用ChaincodeStubInterface.PutState 并将键和值作为参数传入。假设一切顺利,返回一个初始化成功的peer.Response对象。

// Init is called during chaincode instantiation to initialize any
// data. Note that chaincode upgrade also calls this function to reset
// or to migrate data, so be careful to avoid a scenario where you
// inadvertently clobber your ledger's data!
func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response 
  // Get the args from the transaction proposal
  args := stub.GetStringArgs()
  if len(args) != 2 
    return shim.Error("Incorrect arguments. Expecting a key and a value")
  

  // Set up any variables or assets here by calling stub.PutState()

  // We store the key and the value on the ledger
  err := stub.PutState(args[0], []byte(args[1]))
  if err != nil 
    return shim.Error(fmt.Sprintf("Failed to create asset: %s", args[0]))
  
  return shim.Success(nil)


Invoking the Chaincode(调用链码)

            首先我们添加Invoke函数的签名。

// Invoke is called per transaction on the chaincode. Each transaction is
// either a 'get' or a 'set' on the asset created by Init function. The 'set'
// method may create a new asset by specifying a new key-value pair.
func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response 


            与上面的Init函数一样,我们需要从ChaincodeStubInterface中提取参数。Invoke函数的参数将会是要调用的chaincode应用程序的函数的名称。在我们的例子中,我们的应用程序只有两个参数: set和get, 它们允许设置资产的值或检索当前状态。我们首先调用ChaincodeStubInterface.GetFunctionAndParameters来提取链码应用函数的函数名称和参数。

// Invoke is called per transaction on the chaincode. Each transaction is
// either a 'get' or a 'set' on the asset created by Init function. The Set
// method may create a new asset by specifying a new key-value pair.
func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response 
    // Extract the function and args from the transaction proposal
    fn, args := stub.GetFunctionAndParameters()


            接下来, 我们将验证函数名称为set或get,并调用这些chaincode的应用函数。通过shim返回适当的响应, shim.Success或shim.Error函数将响应序列化到gRPC protobuf消息。

// Invoke is called per transaction on the chaincode. Each transaction is
// either a 'get' or a 'set' on the asset created by Init function. The Set
// method may create a new asset by specifying a new key-value pair.
func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response 
    // Extract the function and args from the transaction proposal
    fn, args := stub.GetFunctionAndParameters()

    var result string
    var err error
    if fn == "set" 
            result, err = set(stub, args)
     else 
            result, err = get(stub, args)
    
    if err != nil 
            return shim.Error(err.Error())
    

    // Return the result as success payload
    return shim.Success([]byte(result))

    

Implementing the Chaincode Application(实现链码程序)

            如上所述,我们的链码应用程序实现了两个可以通过Invoke函数调用的函数。现在我们实现这些功能。请注意,正如我们上面提到的,为了访问账本的状态,我们将利用链码的shim API的ChaincodeStubInterface.PutState和ChaincodeStubInterface.GetState函数。

// Set stores the asset (both key and value) on the ledger. If the key exists,
// it will override the value with the new one
func set(stub shim.ChaincodeStubInterface, args []string) (string, error) 
    if len(args) != 2 
            return "", fmt.Errorf("Incorrect arguments. Expecting a key and a value")
    

    err := stub.PutState(args[0], []byte(args[1]))
    if err != nil 
            return "", fmt.Errorf("Failed to set asset: %s", args[0])
    
    return args[1], nil


// Get returns the value of the specified asset key
func get(stub shim.ChaincodeStubInterface, args []string) (string, error) 
    if len(args) != 1 
            return "", fmt.Errorf("Incorrect arguments. Expecting a key")
    

    value, err := stub.GetState(args[0])
    if err != nil 
            return "", fmt.Errorf("Failed to get asset: %s with error: %s", args[0], err)
    
    if value == nil 
            return "", fmt.Errorf("Asset not found: %s", args[0])
    
    return string(value), nil

Pulling it All Together(集合代码)

            最后,我们需要添加主函数,它将调用shim.Start函数。这是整个Chaincode程序的完整代码。

package main

import (
    "fmt"

    "github.com/hyperledger/fabric/core/chaincode/shim"
    "github.com/hyperledger/fabric/protos/peer"
)

// SimpleAsset implements a simple chaincode to manage an asset
type SimpleAsset struct 


// Init is called during chaincode instantiation to initialize any
// data. Note that chaincode upgrade also calls this function to reset
// or to migrate data.
func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response 
    // Get the args from the transaction proposal
    args := stub.GetStringArgs()
    if len(args) != 2 
            return shim.Error("Incorrect arguments. Expecting a key and a value")
    

    // Set up any variables or assets here by calling stub.PutState()

    // We store the key and the value on the ledger
    err := stub.PutState(args[0], []byte(args[1]))
    if err != nil 
            return shim.Error(fmt.Sprintf("Failed to create asset: %s", args[hyperledger fabric链码开发(Chaincode for Developers)

Hyperledger Fabric V1.4Javascript链码开发常用API

HyperLeger Fabric开发——HyperLeger Fabric链码开发测试

Hyperledger Fabric链码之一

Hyperledger Fabric链码之二

Hyperledger Fabric 链码生命周期