Hyperledger-Fabric · 2018年1月17日 0

Hyperledger-Fabric安装开发部署(三)智能合约

简介

  • Hyperledger Fabric是分布式账本解决方案的平台,以模块化架构为基础,提供高度的机密性,弹性,灵活性和可扩展性。它旨在支持不同组件的可插拔实现,并适应经济生态系统中存在的复杂性性。
  • Hyperledger Fabric提供独特的弹性和可扩展架构,区别于其他区块链解决方案。在未来的规划中,对于企业区块链将完全建立在审核且开源架构之上; Hyperledger Fabric是你最好的起点。

官方英文版教程:
http://hyperledger-fabric.readthedocs.io/en/latest/index.html

本来此篇想介绍nodejs-SDK相关的内容,但发现没有对Fabric本身没有太多的介绍,所以继续补充基本的内容。
主要以概念与代码相结合的方式解释相关内容。


1. 什么是智能合约

再解释智能合约之前,让我们先从合约开始。

1.1 合约

简单说是双方签订某种合作协议,各自履行各自的义务,来达成双方的利益。在这里生活中一般都会用合同的形式,签字盖章,由法律保护。

1.2 传统智能合约

把生活中的合约放到代码的世界里,那么就是由编写好的代码执行我们的合约内容;举个例子,支付宝有个功能每月工资自动充值,合约的内容就是到了设定的时间,执行扣款的动作,之后再到余额宝或者理财产品中。完全数字化的合约,是非常便捷的。
也就是智能合约就是针对特定角色定制的执行某件事情的代码。
好了那么到这里,就会有很多的问题,还是说上边的例子;支付宝多扣款,其他时间扣款,不履行充值到理财产品中怎么办?等等一系类的问题,这些问题都是一个问题支付宝有没有可能不履行、违规执行我们的合约?答案是有可能,但是支付宝还有人充钱,因为我们信任支付宝,我们相信支付宝不会违约。但是其他小公司呢?这里就充满了未知,说白了就是没有信任,一旦违约,所有的数据都在别人的服务器,我们啥都没有。其次就是系统本身本黑客攻击,把钱转走了,造成损失。

1.3 区块链中的智能合约

区块链本身最重要的一点是解决信任问题,传统的智能合约中,出现的问题也是信任问题。
这里就要说到区块链的几个特性,去中心化、开放性、自治性、不可篡改、匿名性:

  • 由于使用分布式核算和存储,不存在中心化的硬件或管理机构,任意节点的权利和义务都是均等的,系统中的数据块由整个系统中具有维护功能的节点来共同维护;
  • 系统是开放的,除了交易各方的私有信息被加密外,区块链的数据对所有人公开,任何人都可以通过公开的接口查询区块链数据和开发相关应用,因此整个系统信息高度透明;
  • 区块链采用基于协商一致的规范和协议(比如一套公开透明的算法)使得整个系统中的所有节点能够在去信任的环境自由安全的交换数据,使得对“人”的信任改成了对机器的信任,任何人为的干预不起作用;
  • 一旦信息经过验证并添加至区块链,就会永久的存储起来,除非能够同时控制住系统中超过51%的节点,否则单个节点上对数据库的修改是无效的,因此区块链的数据稳定性和可靠性极高。
  • 由于节点之间的交换遵循固定的算法,其数据交互是无需信任的(区块链中的程序规则会自行判断活动是否有效),因此交易对手无须通过公开身份的方式让对方自己产生信任,对信用的累积非常有帮助。

2. Hyperledger Fabric中的智能合约

我们所说的ChainCode就是智能合约的代码实现。
从技术上讲,ChainCode是一段在区块链上存储、验证和执行的代码。从业务上讲,这段代码逻辑可被视为法律合同的补充物、替代物,即“智能合约”。所以说,ChainCode是HyperLedger Fabric中智能合约的代码实现,开发者可以使用ChainCode开发商业合约,资产定义,以及去中心化应用等,是区块链应用中的核心逻辑所在。

目前Chaincode编写使用的语言有Golang、Java,预计今后会加入其他的高级语言。需要注意的是,使用SDK调用链码的时候需要确定链码所使用的语言,语言的选择是一个基本参数,默认是Golang。

3. Chaincode基本结构

package main

import "fmt"
//这里引用了shim包;shim 包 提供了一些 API,以便您的链代码与底层区块链网络交互来访问状态变量、交易上下文、调用方证书和属性,并调用其他链代码和执行其他操作。
import "github.com/hyperledger/fabric/core/chaincode/shim"

//这里的SampleChaincode就是类名称了
type SampleChaincode struct {
}

func (t *SampleChaincode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
    return nil, nil
}

func (t *SampleChaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
    return nil, nil
}

func (t *SampleChaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
    return nil, nil
}

//任何 Go 程序的起点都是 main 函数,因此该函数被用于引导/启动链代码。当对等节点部署其链代码实例时,就会执行 main 函数。
func main() {
    err := shim.Start(new(SampleChaincode))
    if err != nil {
        fmt.Println("Could not start SampleChaincode")
    } else {
        fmt.Println("SampleChaincode successfully started")
    }

}

SampleChaincode 是实现 shim.Chaincode 接口所需的结构,它必须包含 3 种方法 Init、Query 和 Invoke 才能被 shim 包视为有效的链代码类型。让我们逐一介绍这 3 种方法:

Init 方法
Init 方法 在链代码首次部署到区块链网络时调用,将由部署自己的链代码实例的每个对等节点执行。此方法可用于任何与初始化、引导或设置相关的任务。

Query 方法
只要在区块链状态上执行任何读取/获取/查询操作,就会调用 Query 方法。根据链代码的复杂性,此方法含有您的读取/获取/查询逻辑,或者它可以外包给可从 Query 方法内调用的不同方法。

Query 方法不会更改底层链代码的状态,因此它不会在交易上下文内运行。如果尝试在 Query 方法内修改区块链的状态,将出现一个错误显示缺少交易上下文。另外,因为此方法仅用于读取区块链的状态,所以对它的调用不会记录在区块链上。

Invoke 方法
只要修改区块链的状态,就会调用 Invoke 方法。简言之,所有创建、更新和删除操作都应封装在 Invoke 方法内。因为此方法将修改区块链的状态,所以区块链 Fabric 代码会自动创建一个交易上下文,以便此方法在其中执行。对此方法的所有调用都会在区块链上记录为交易,这些交易最终被写入区块中。

至于每个方法中的业务逻辑那就要大家自己编写;
上一篇文章中有简单的介绍e2e项目中Chaincode相关内容。

4. Chaincode中的数据模型

Hyperledger 账本包含两部分:

  • World State,存储在一个键值存储中。这个键值存储由 RocksDB 提供支持。这个键值存储获取一个字节数组作为值,该值可用于存储序列化 JSON 结构。基本上讲,这个键值存储可用于存储您的智能合约正常运行所需的所有自定义数据模型/模式。
  • 区块链,由一系列区块组成,每个区块包含许多交易。每个区块包含 World State 的哈希值,并被链接到前一个区块。区块链采用仅附加模式 (append-only)。

总的来说就是我们使用区块链产生的所有数据中可以分为两种:

  1. 我们自己需要保存、提交的业务数据
  2. 区块链本身的区块、交易的基本信息

与Java、C#等等语言一样,Go中也需要建立一些Model供我们使用:

type PersonalInfo struct {
    Firstname string `json:"firstname"`
    Lastname  string `json:"lastname"`
    Email     string `json:"email"`
    Mobile    string `json:"mobile"`
}
type LoanApplication struct {
    ID                     string        `json:"id"`
    PropertyId             string        `json:"propertyId"`
    LandId                 string        `json:"landId"`
    PermitId               string        `json:"permitId"`
    BuyerId                string        `json:"buyerId"`
    SalesContractId        string        `json:"salesContractId"`
    PersonalInfo           PersonalInfo  `json:"personalInfo"`
    ....
    ....
}

因为我们的键值存储将数据存储为 JSON,所以这些数据模型最终需要转换为 JSON 字符串。每个字段(如 json:"id")的注释的作用类似于编组 (marshal) /解组 (unmarshal) API 的元数据,该 API 将使用这些注释将每个字段映射到相应的 JSON 字符串等效表示。

5. Chaincode存储和查询数据

简单的回顾一下e2e中的Chaincode

func (t *SimpleChaincode) query(stub shim.ChaincodeStubInterface, args []string) pb.Response {
    var A string // Entities
    var err error

    if len(args) != 1 {
        return shim.Error("Incorrect number of arguments. Expecting name of the person to query")
    }

    A = args[0]

    // Get the state from the ledger
    Avalbytes, err := stub.GetState(A)
    if err != nil {
        jsonResp := "{\"Error\":\"Failed to get state for " + A + "\"}"
        return shim.Error(jsonResp)
    }

    if Avalbytes == nil {
        jsonResp := "{\"Error\":\"Nil amount for " + A + "\"}"
        return shim.Error(jsonResp)
    }

    jsonResp := "{\"Name\":\"" + A + "\",\"Amount\":\"" + string(Avalbytes) + "\"}"
    fmt.Printf("Query Response:%s\n", jsonResp)
    return shim.Success(Avalbytes)
}

插入、查询其实就是

stub.GetState(A);
stub.PutState(A, []byte(value));

其他的都是基本校验、各自的业务逻辑;
注意,存储在键值存储中的值必须始终是字节数组。因此,在存储到账本之前,字符串首先被转换为字节数组。取数据时同样的要把字节数组转回来。

在引用了encoding/json包后,可以使用bytes, err ;= json.Marshal(model)把一个对象也转化为字节数组。

在e2e的例子中Chaincode的比较简单,建议学习Fabric-Car项目,它的Chaincode的功能相对完整清晰一些。
https://github.com/hyperledger/fabric-samples

.
.
.
.
.
.
.
【本文章出自NM1024.com,转载请注明作者出处。】






>>转载请注明原文链接地址:Hyperledger-Fabric安装开发部署(三)智能合约