• 007-链码


    一、参考地址

    golang

    nodejs:https://fabric-sdk-node.github.io/

    java:https://github.com/hyperledger/fabric-sdk-java

    https://hyperledger-fabric.readthedocs.io/en/latest/chaincode4ade.html

    二、概述

    2.1、概念

      Chaincode是一个用Go编写的程序,后期也将支持Java中实现了一个规定的接口。

      链码运行在与承认对等体进程隔离的安全Docker容器中。 Chaincode通过应用程序提交的交易来初始化和管理分类帐状态。

      链码通常处理网络成员所同意的业务逻辑,因此可被视为“智能合同”。 由链码创建的状态仅限于该链码,不能被另一个链码直接访问。 然而,在同一个网络中,给定适当的权限,链码可以调用另一个链码来访问其状态。

    2.2、两个角色

      我们对链码提供两个不同的观点。 一个,从应用程序开发人员的角度来看,开发一个名为Chaincode for Developers的块链应用程序/解决方案,另一个是针对负责管理块链网络的块链网络运营商的运营商链码,以及谁将利用Hyperledger Fabric API 链式代码的安装,实例化和升级,但可能不会涉及开发链码应用程序。

    2.3、api

      每个链码程序都必须实现Chaincode接口,其响应于接收到的事务调用其方法。 特别地,当链码接收到instantiate 实例化或upgrade 升级事务时,调用Init方法,使得链码可以执行任何必要的初始化,包括应用程序状态的初始化。 调用Invoke方法响应于接收到一个调用事务来处理事务提议。

      初始化:http://godoc.org/github.com/hyperledger/fabric/core/chaincode/shim#Chaincode

      chaincode“shim”API中的另一个接口是ChaincodeStubInterface,用于访问和修改分类帐,并在链码之间进行调用。

      api:http://godoc.org/github.com/hyperledger/fabric/core/chaincode/shim#ChaincodeStub

    2.3.1、简单资产链码

      基本键值对链码

    1、选择代码的位置

      安装go,并且配置了环境变量

      现在,要为chaincode应用程序创建一个名为$ GOPATH / src /的子目录的目录。 以及创建源文件 

    mkdir -p $GOPATH/src/sacc && cd $GOPATH/src/sacc
    touch sacc.go

    2、必要的包

    package main
    
    import (
        "fmt"
    
        "github.com/hyperledger/fabric/core/chaincode/shim"
        "github.com/hyperledger/fabric/protos/peer"
    )

    3、初始化链码

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

      注意,chaincode升级还会调用此功能。 当编写一个将升级现有的链码时,请确保适当地修改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")
      }
    }
    View Code

      接下来,现在我们已经确定了调用有效,我们将把初始状态存储在分类帐中。为此,我们将使用键和值作为参数传递给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)
    }
    View Code

    4、调用链码

    首先,我们添加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()
    }

      接下来,我们将函数名称设置为get或get,并调用这些链代码应用程序函数,通过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))
    }
    View Code

    5、实现链码应用

      如上所述,我们的chaincode应用程序实现了可以通过Invoke函数调用的两个函数。现在我们来实现这些功能。请注意,如上所述,为了进入分类帐状态,我们将利用chaincode shim API的ChaincodeStubInterface.PutStateChaincodeStubInterface.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
    }
    View Code

    6、结合到一起

    最后,我们需要添加main函数,这将调用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[0]))
        }
        return shim.Success(nil)
    }
    
    // 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 { // assume 'get' even if fn is nil
                result, err = get(stub, args)
        }
        if err != nil {
                return shim.Error(err.Error())
        }
    
        // Return the result as success payload
        return shim.Success([]byte(result))
    }
    
    // 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
    }
    
    // main function starts up the chaincode in the container during instantiate
    func main() {
        if err := shim.Start(new(SimpleAsset)); err != nil {
                fmt.Printf("Error starting SimpleAsset chaincode: %s", err)
        }
    }
    View Code

    7、编译链码

    go get -u --tags nopkcs11 github.com/hyperledger/fabric/core/chaincode/shim
    go build --tags nopkcs11

    8、测试使用开发模式

      通常链式代码由对等体启动和维护。然而,在“开发模式”中,链码由用户构建和启动。在快速代码/构建/运行/调试周期周转期间的链码开发阶段,此模式非常有用。

      我们通过利用样例开发网络的预生成的订单和渠道工件来启动“开发模式”。因此,用户可以立即跳入编译链码和调用的过程。

    9、安装Hyperledger Fabric 示例

    参看05/06中安装https://hyperledger-fabric.readthedocs.io/en/latest/samples.html

    转到fabric-samples的chaincode-docker-devmode目录:

    cd chaincode-docker-devmode
    10、下载Docker image

      我们需要四个Docker图像,以便“开发模式”针对提供的脚本运行。如果您安装了fabric-samples repo克隆,并遵循下载指令 - 特定于二进制文件的说明,那么您应该在本地安装必要的Docker映像

    docker images
    REPOSITORY                     TAG                                  IMAGE ID            CREATED             SIZE
    hyperledger/fabric-tools       latest                               e09f38f8928d        4 hours ago         1.32 GB
    hyperledger/fabric-tools       x86_64-1.0.0                         e09f38f8928d        4 hours ago         1.32 GB
    hyperledger/fabric-orderer     latest                               0df93ba35a25        4 hours ago         179 MB
    hyperledger/fabric-orderer     x86_64-1.0.0                         0df93ba35a25        4 hours ago         179 MB
    hyperledger/fabric-peer        latest                               533aec3f5a01        4 hours ago         182 MB
    hyperledger/fabric-peer        x86_64-1.0.0                         533aec3f5a01        4 hours ago         182 MB
    hyperledger/fabric-ccenv       latest                               4b70698a71d3        4 hours ago         1.29 GB
    hyperledger/fabric-ccenv       x86_64-1.0.0                         4b70698a71d3        4 hours ago         1.29 GB
    View Code

    现在打开三个终端,并导航到每个的chaincode-docker-devmode目录。

    终端一、开启网络

    docker-compose -f docker-compose-simple.yaml up

      以上使用SingleSampleMSPSolo订户配置文件启动网络,并以“开发模式”启动对等体。它还启动两个额外的容器 - 一个用于链码环境和一个与链码交互的CLI。用于创建和连接通道的命令嵌入在CLI容器中,因此我们可以立即跳转到链码调用。

    终端二、构建并启动链码

    docker exec -it chaincode bash 
    #后
    root@d2629980e76b:/opt/gopath/src/chaincode#
    cd sacc
    go build

    运行链码

    CORE_PEER_ADDRESS=peer:7051 CORE_CHAINCODE_ID_NAME=mycc:0 ./sacc

    链码从对等体和链码日志开始,表示成功注册对等体。请注意,在此阶段,链码与任何频道都不相关。这在后续步骤中使用实例化命令完成。

    终端三、使用链码

    即使您处于--peer-chaincodedev模式,您仍然必须安装链码,以便生命周期系统链码可以正常检查。在-peer-chaincodedev模式下,此要求可能会被删除。

    我们将利用CLI容器来驱动这些调用。

    docker exec -it cli bash
    peer chaincode install -p chaincodedev/chaincode/sacc -n mycc -v 0
    peer chaincode instantiate -n mycc -v 0 -c '{"Args":["a","10"]}' -C myc

    使用

    peer chaincode invoke -n mycc -c '{"Args":["set", "a", "20"]}' -C myc
    peer chaincode query -n mycc -c '{"Args":["query","a"]}' -C myc
  • 相关阅读:
    【Mac + Appium + Java1.8学习(三)】之IOS自动化环境安装配置以及简单测试用例编写(模拟器、真机)
    【Mac + Appium + Java1.8学习(一)】之Android自动化环境安装配置以及IDEA配置(附录扩展Selenium+Java自动化)
    【Mac + Appium + Python3.6学习(六)】之安装Android模拟器(Genymotion)并运行模拟器进行自动化
    【Mac + Appium + Python3.6学习(五)】之常用的Android自动化测试API总结
    【Mac + Appium + Python3.6学习(四)】之常用的IOS自动化测试API总结
    【Mac + Appium + Python3.6学习(三)】之IOS自动化测试环境配置
    【Mac + Appium + Python3.6学习(二)】之Android自动化测试,appium-desktop配置和简易自动化测试脚本
    【Mac + Appium学习(一)】之安装Appium环境前提准备
    【Mac + Python3.6 + ATX基于facebook-wda】之IOS自动化(三):facebook-wda库--API学习以及附录:Github上对WDA的问题解答
    【转】【Mysql学习】之Mac上用终端使用mySQL
  • 原文地址:https://www.cnblogs.com/bjlhx/p/7204729.html
Copyright © 2020-2023  润新知