黄先森

西二旗民工

分享一些与编程、分布式系统、区块链技术相关的内容


欢迎访问个人github

chaincode开发、调试教程以及api介绍

Chaincode教程

1. 概要

1.1 什么是Chaincode?

Chaincode是一个程序,用Go,node.js,java等编程语言编写,并实现了特定的接口(后面会详细介绍,分别为InitInvoke)。Chaincode在一个安全的Docker容器中运行,该容器与背书peer进程隔离。Chaincode通过应用程序提交的事务来初始化和管理账本状态。

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

1.2 两种角色

我们可以从两种不同的角色来认识chaincode。一个是从应用程序开发人员的角度出发,应用开发者会开发一个名为Chaincode for Developers的区块链应用程序/解决方案;另一个是面向区块链网络运维人员Chaincode for Operators,区块链网络运维人员负责管理区块链网络,并利用Hyperledger Fabric API来安装、实例化和升级chaincode,但很可能不会涉及chaincode应用程序的开发。

下面我们将分别从chaincode开发者和运维人员两方面对chaincode做一个较为详细的介绍,最后通过结合源码分析,加深对chaincode的理解。最后希望能帮助chaincode开发者能快速上手chaincode的开发,还有帮助chaincode运维人员能够保证chaincode能正常的运行。

2. chaincode开发者教程

2.1 chaincode API

每个chaincode程序必须实现Chaincode接口

Chaincode接口被调用以回应接收到的事务。特别是当chaincode接收instantiateupgrade事务时,会调用Init方法,以便chaincode可以执行任何必要的初始化,包括应用程序状态的初始化。Invoke方法是为了响应接收调用事务来处理事务提案。PS:当通过命令行方式peer chaincode invoke XXX 就会调用指定chaincode重写的Invoke方法。

chaincode “shim” API的另外一个接口是ChaincodeStubInterface:

这个接口用于访问和修改区块账本,并在chaincode之间能够互相调用。

在本教程中,我们将通过实现一个用于管理简单“资产”的简单chiancode应用来描述如何使用这些API,所有API请参考文末的图1。如GetStatePutStateGetHistoryForKeyCreateCompositeKeyGetStateByPartialCompositeKeyDelState等等,通过这些API基本能完成传统关系型数据的增删改查操作。本教程会在最后分析这些接口具体是什么,怎么用,实现原理是什么。希望能帮助读者更加深入了解chaincode,并能根据自身业务场景选择合适的API,保证区块链有较高的存储速度时,空间效率也不会太低。

2.2 一个简单示例:“资产管理” chaincode

我们的应用程序是一个基本示例chaincode,用于在账本上创建资产(键值对)。

2.2.1 选择代码的位置

如果你之前没用过Go语言编程,你可能需要确保已安装Go编程语言并且已正确设置好GO的开发环境。

现在,你需要为chaincode应用程序创建一个目录作为$GOPATH/src/的子目录。

为了简单起见,我们使用下面的命令:

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

现在,让我们创建即将补充代码的源文件:

touch sacc.go

2.2.2 基本框架

首先,我们从chaincode的基本框架开始。每个chaincode都一样,都会实现chaincode接口,即InitInvoke函数。因此,让我们添加go import语句以获取chaincode的必要依赖代码(类似于java的import、c语言的#include)。我们将导入chaincode shim包和peer protobuf package。接下来,让我们添加一个结构SimpleAsset作为chaincode 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 {
}

2.2.3 初始化chaincode

接下来,我们将实现Init函数。

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

}

Note.请注意,chaincode升级也会调用此函数。在编写将升级现有chaincode的chiancode的时候,请确保是当地修改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,并将key、value作为参数传入。假设一切顺利,返回一个指示初始化成功的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)
}

调用 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 {

}

3. chaincode运维者教程


4. 参考

 图 1 chaincode api类图

最近的文章

(转载)【区块链】一文看懂区块链:一步一步发明比特币

<!DOCTYPE html><html><head><meta charset="utf-8"><title>【区块链】一文看懂区块链:一步一步发明比特币 | Go Further | Stay Hungry, Stay Foolish</title><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale...…

继续阅读
更早的文章

(转载)加密货币的本质 阮一峰的网络日志

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> 加密货币的本质 - 阮一峰的网络日志 阮一峰的网络日志 » 首页 » 档案 ...…

继续阅读