链码(chaincode) 会对 Fabric应用程序  发送的交易做出响应,执行代码逻辑,与 账本 进行交互。

再复习下他们之间的逻辑关系:
Hyperledger Fabric 中,Chaincode 默认运行在 Docker 容器中。
Peer 通过调用 Docker API 来创建和启动 Chaincode 容器。
Chaincode 容器启动后跟 Peer 之间创建 gRPC 连接,双方通过发送 ChaincodeMessage 来进行交互通信。
Chaincode 容器利用 core.chaincode.shim 包提供的接口来向 Peer 发起请求。

每个chaincode程序都必须实现chaincode接口,接口中的方法会在响应传来的交易时被调用。

type Chaincode interface {
Init (stub ChaincodeStubInterface) pb.Response
Invoke (stub ChaincodeStubInterface) pb.Response
}
  • Init(初始化)方法会在chaincode接收到instantiate(实例化)或者upgrade(升级)交易时被调用,进而使得chaincode顺利执行必要的初始化操作,包括初始化应用的状态。
  • Invoke(调用)方法会在响应invoke(调用)交易时被调用以执行交易。

现在,Fabric支持多种计算机语言实现的链码,包括Golang、JavaScript、Java等。

一个链码的必要结构

package main

//(1)引入必要的包
//fmt是golang系统提供的通用输入输出包,后面两个包是必须的。
//第二个包 shim包是Fabric系统提供的上下文环境,包含了Chaincode和Fabric交互的接口,在Chaincode中,执行赋值、查询等功能都需要通过shim。
import (
"fmt"
"github.com/hyperledger/fabric/core/chaincode/shim"
   pb "github.com/hyperledger/fabric/protos/peer"
)
//(2)声明一个结构体,即chaincode的主体结构体
//该结构体需要实现Fabric提供的接口
"github.com/hyperledger/fabric/protos/peer",其中必须实现下面的两个方法。
type DemoChaincode struct { } //(3)Init() 和 Invoke() 函数的实现,在其中利用 shim.ChaincodeStubInterface 结构,实现跟账本的交互逻辑。
func (t *DemoChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
  // 该方法中实现链码初始化或升级是的处理逻辑
  // 编写时可以灵活使用stub中的API
 return stub.Success(nil)
}
func (t *DemoChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response
//该方法中实现链码运行中被调用或查询时的处理逻辑
  //编写时可以灵活使用stub中的API
return stub.Success(nil)
}

//(4)主函数,需要调用shim.Start()方法
func main() {
err := shim.Start(new(DemoChaincode))
if err != nil {
fmt.Printf("Error starting DemoChaincode: %s", err)
}
}

ChainCode编写的步骤

这个实例是 fabric-sample/fabcar目录下:有一个关于car 交易的 app

1)引入相关包

首先,我们先进行准备工作。对于每一个chaincode,它都会实现预定义的chaincode接口,特别是Init和Invoke函数接口。所以我们首先为我们的chaincode引入必要的依赖。这里的shim层是节点与链码交互的中间层。如下图所示:

package main

import (
"bytes"
"encoding/json"
"fmt"
"strconv"
"github.com/hyperledger/fabric/core/chaincode/shim"
sc "github.com/hyperledger/fabric/protos/peer"
)

2)初始化Chaincode

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

func (s *SmartContract) Init(APIstub shim.ChaincodeStubInterface) sc.Response {
return shim.Success(nil)
}

值得留意的是chaincode升级同样会调用该函数。当我们编写的chaincode会升级现有chaincode时,需要确保适当修正Init函数。特别地,如果没有“迁移”操作或其他需要在升级中初始化的东西,那么就提供一个空的“Init”方法。我们这里仅仅提供了一个简单的Init方法。

3)编写Chaincode接口函数

首先,添加Invoke函数签名

func (s *SmartContract) Invoke(APIstub shim.ChaincodeStubInterface) sc.Response {}

我们需要调用ChaincodeStubInterface来获取参数。我们将调用ChaincodeStubInterface并以键值为参数传入。如果一切正常,那么我们会收到表明初始化成功的peer.Response返回对象。Invoke函数所需的传入参数正是应用想要调用的chaincode的名称。在我们的应用里面,我们有几个简单的功能函数:queryCar, initLedger, createCar, queryAllCars, changeCarOwner等。

下面,我们将使这几个函数名正式生效,并调用这些chaincode应用函数,经由shim.Successshim.Error函数返回一个合理的响应。这两个shim成员函数可以将响应序列化为gRPC protobuf消息。

func (s *SmartContract) Invoke(APIstub shim.ChaincodeStubInterface) sc.Response {
function, args := APIstub.GetFunctionAndParameters()
if function == "queryCar" {
return s.queryCar(APIstub, args)
} else if function == "initLedger" {
return s.initLedger(APIstub)
} else if function == "createCar" {
return s.createCar(APIstub, args)
} else if function == "queryAllCars" {
return s.queryAllCars(APIstub)
} else if function == "changeCarOwner" {
return s.changeCarOwner(APIstub, args)
} return shim.Error("Invalid Smart Contract function name.")
}

4)编写Chaincode的调用函数

如上文所述,我们的chaincode应用实现了五个函数,并可以被Invoke函数调用。下面我们就来真正实现这些函数。注意,就像上文一样,我们调用chaincode shim API中的ChaincodeStubInterface.PutState和ChaincodeStubInterface.GetState函数来访问账本。

func (s *SmartContract) queryCar(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {

    if len(args) !=  {
return shim.Error("Incorrect number of arguments. Expecting 1")
} carAsBytes, _ := APIstub.GetState(args[])
return shim.Success(carAsBytes)
} func (s *SmartContract) initLedger(APIstub shim.ChaincodeStubInterface) sc.Response {
cars := []Car{
Car{Make: "Toyota", Model: "Prius", Colour: "blue", Owner: "Tomoko"},
Car{Make: "Ford", Model: "Mustang", Colour: "red", Owner: "Brad"},
Car{Make: "Hyundai", Model: "Tucson", Colour: "green", Owner: "Jin Soo"},
Car{Make: "Volkswagen", Model: "Passat", Colour: "yellow", Owner: "Max"},
Car{Make: "Tesla", Model: "S", Colour: "black", Owner: "Adriana"},
Car{Make: "Peugeot", Model: "", Colour: "purple", Owner: "Michel"},
Car{Make: "Chery", Model: "S22L", Colour: "white", Owner: "Aarav"},
Car{Make: "Fiat", Model: "Punto", Colour: "violet", Owner: "Pari"},
Car{Make: "Tata", Model: "Nano", Colour: "indigo", Owner: "Valeria"},
Car{Make: "Holden", Model: "Barina", Colour: "brown", Owner: "Shotaro"},
} i :=
for i < len(cars) {
fmt.Println("i is ", i)
carAsBytes, _ := json.Marshal(cars[i])
APIstub.PutState("CAR"+strconv.Itoa(i), carAsBytes)
fmt.Println("Added", cars[i])
i = i +
} return shim.Success(nil)
} func (s *SmartContract) createCar(APIstub shim.ChaincodeStubInterface, args []string) sc.Response { if len(args) != {
return shim.Error("Incorrect number of arguments. Expecting 5")
} var car = Car{Make: args[], Model: args[], Colour: args[], Owner: args[]} carAsBytes, _ := json.Marshal(car)
APIstub.PutState(args[], carAsBytes) return shim.Success(nil)
} func (s *SmartContract) queryAllCars(APIstub shim.ChaincodeStubInterface) sc.Response { startKey := "CAR0"
endKey := "CAR999" resultsIterator, err := APIstub.GetStateByRange(startKey, endKey)
if err != nil {
return shim.Error(err.Error())
}
defer resultsIterator.Close() // buffer is a JSON array containing QueryResults
var buffer bytes.Buffer
buffer.WriteString("[") bArrayMemberAlreadyWritten := false
for resultsIterator.HasNext() {
queryResponse, err := resultsIterator.Next()
if err != nil {
return shim.Error(err.Error())
}
// Add a comma before array members, suppress it for the first array member
if bArrayMemberAlreadyWritten == true {
buffer.WriteString(",")
}
buffer.WriteString("{\"Key\":")
buffer.WriteString("\"")
buffer.WriteString(queryResponse.Key)
buffer.WriteString("\"") buffer.WriteString(", \"Record\":")
// Record is a JSON object, so we write as-is
buffer.WriteString(string(queryResponse.Value))
buffer.WriteString("}")
bArrayMemberAlreadyWritten = true
}
buffer.WriteString("]") fmt.Printf("- queryAllCars:\n%s\n", buffer.String()) return shim.Success(buffer.Bytes())
} func (s *SmartContract) changeCarOwner(APIstub shim.ChaincodeStubInterface, args []string) sc.Response { if len(args) != {
return shim.Error("Incorrect number of arguments. Expecting 2")
} carAsBytes, _ := APIstub.GetState(args[])
car := Car{} json.Unmarshal(carAsBytes, &car)
car.Owner = args[] carAsBytes, _ = json.Marshal(car)
APIstub.PutState(args[], carAsBytes) return shim.Success(nil)
} func main() { // Create a new Smart Contract
err := shim.Start(new(SmartContract))
if err != nil {
fmt.Printf("Error creating new Smart Contract: %s", err)
}
}

这里可以看到,在最后关头我们写了main函数,它将调用shim.Start 函数,main函数的作用,是在容器里启动chaincode。

5)整合全部代码

将上面的代码整合在一起如下:

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/ /*
* The sample smart contract for documentation topic:
* Writing Your First Blockchain Application
*/ package main /* Imports
* 4 utility libraries for formatting, handling bytes, reading and writing JSON, and string manipulation
* 2 specific Hyperledger Fabric specific libraries for Smart Contracts
*/
import (
"bytes"
"encoding/json"
"fmt"
"strconv" "github.com/hyperledger/fabric/core/chaincode/shim"
sc "github.com/hyperledger/fabric/protos/peer"
) // Define the Smart Contract structure
type SmartContract struct {
} // Define the car structure, with 4 properties. Structure tags are used by encoding/json library
type Car struct {
Make string `json:"make"`
Model string `json:"model"`
Colour string `json:"colour"`
Owner string `json:"owner"`
} /*
* The Init method is called when the Smart Contract "fabcar" is instantiated by the blockchain network
* Best practice is to have any Ledger initialization in separate function -- see initLedger()
*/
func (s *SmartContract) Init(APIstub shim.ChaincodeStubInterface) sc.Response {
return shim.Success(nil)
} /*
* The Invoke method is called as a result of an application request to run the Smart Contract "fabcar"
* The calling application program has also specified the particular smart contract function to be called, with arguments
*/
func (s *SmartContract) Invoke(APIstub shim.ChaincodeStubInterface) sc.Response { // Retrieve the requested Smart Contract function and arguments
function, args := APIstub.GetFunctionAndParameters()
// Route to the appropriate handler function to interact with the ledger appropriately
if function == "queryCar" {
return s.queryCar(APIstub, args)
} else if function == "initLedger" {
return s.initLedger(APIstub)
} else if function == "createCar" {
return s.createCar(APIstub, args)
} else if function == "queryAllCars" {
return s.queryAllCars(APIstub)
} else if function == "changeCarOwner" {
return s.changeCarOwner(APIstub, args)
} return shim.Error("Invalid Smart Contract function name.")
} func (s *SmartContract) queryCar(APIstub shim.ChaincodeStubInterface, args []string) sc.Response { if len(args) != {
return shim.Error("Incorrect number of arguments. Expecting 1")
} carAsBytes, _ := APIstub.GetState(args[])
return shim.Success(carAsBytes)
} func (s *SmartContract) initLedger(APIstub shim.ChaincodeStubInterface) sc.Response {
cars := []Car{
Car{Make: "Toyota", Model: "Prius", Colour: "blue", Owner: "Tomoko"},
Car{Make: "Ford", Model: "Mustang", Colour: "red", Owner: "Brad"},
Car{Make: "Hyundai", Model: "Tucson", Colour: "green", Owner: "Jin Soo"},
Car{Make: "Volkswagen", Model: "Passat", Colour: "yellow", Owner: "Max"},
Car{Make: "Tesla", Model: "S", Colour: "black", Owner: "Adriana"},
Car{Make: "Peugeot", Model: "", Colour: "purple", Owner: "Michel"},
Car{Make: "Chery", Model: "S22L", Colour: "white", Owner: "Aarav"},
Car{Make: "Fiat", Model: "Punto", Colour: "violet", Owner: "Pari"},
Car{Make: "Tata", Model: "Nano", Colour: "indigo", Owner: "Valeria"},
Car{Make: "Holden", Model: "Barina", Colour: "brown", Owner: "Shotaro"},
} i :=
for i < len(cars) {
fmt.Println("i is ", i)
carAsBytes, _ := json.Marshal(cars[i])
APIstub.PutState("CAR"+strconv.Itoa(i), carAsBytes)
fmt.Println("Added", cars[i])
i = i +
} return shim.Success(nil)
} func (s *SmartContract) createCar(APIstub shim.ChaincodeStubInterface, args []string) sc.Response { if len(args) != {
return shim.Error("Incorrect number of arguments. Expecting 5")
} var car = Car{Make: args[], Model: args[], Colour: args[], Owner: args[]} carAsBytes, _ := json.Marshal(car)
APIstub.PutState(args[], carAsBytes) return shim.Success(nil)
} func (s *SmartContract) queryAllCars(APIstub shim.ChaincodeStubInterface) sc.Response { startKey := "CAR0"
endKey := "CAR999" resultsIterator, err := APIstub.GetStateByRange(startKey, endKey)
if err != nil {
return shim.Error(err.Error())
}
defer resultsIterator.Close() // buffer is a JSON array containing QueryResults
var buffer bytes.Buffer
buffer.WriteString("[") bArrayMemberAlreadyWritten := false
for resultsIterator.HasNext() {
queryResponse, err := resultsIterator.Next()
if err != nil {
return shim.Error(err.Error())
}
// Add a comma before array members, suppress it for the first array member
if bArrayMemberAlreadyWritten == true {
buffer.WriteString(",")
}
buffer.WriteString("{\"Key\":")
buffer.WriteString("\"")
buffer.WriteString(queryResponse.Key)
buffer.WriteString("\"") buffer.WriteString(", \"Record\":")
// Record is a JSON object, so we write as-is
buffer.WriteString(string(queryResponse.Value))
buffer.WriteString("}")
bArrayMemberAlreadyWritten = true
}
buffer.WriteString("]") fmt.Printf("- queryAllCars:\n%s\n", buffer.String()) return shim.Success(buffer.Bytes())
} func (s *SmartContract) changeCarOwner(APIstub shim.ChaincodeStubInterface, args []string) sc.Response { if len(args) != {
return shim.Error("Incorrect number of arguments. Expecting 2")
} carAsBytes, _ := APIstub.GetState(args[])
car := Car{} json.Unmarshal(carAsBytes, &car)
car.Owner = args[] carAsBytes, _ = json.Marshal(car)
APIstub.PutState(args[], carAsBytes) return shim.Success(nil)
} // The main function is only relevant in unit test mode. Only included here for completeness.
func main() { // Create a new Smart Contract
err := shim.Start(new(SmartContract))
if err != nil {
fmt.Printf("Error creating new Smart Contract: %s", err)
}
}

调试ChainCode

在Fabric中,调用chainCode有两种方式,一种是通过SDK编写应用程序来调用,比如fabcar项目中,通过nodejs程序调用ChainCode。还有一种方式,就是使用cli命令来调用ChainCode。

1)应用程序编写

fabric-sample/fabcar目录下:有一个关于car 交易的 app

https://www.tuoluocaijing.cn/article/detail-45754.html

https://segmentfault.com/a/1190000014350821

https://www.jianshu.com/p/b0c11a76ff67

2)cli命令调用

更详细的说明见:

《区块链核心技术与应用》

《区块链开发实战》

Hyperledger Fabric(5)ChainCode的编写步骤的更多相关文章

  1. Hyperledger Fabric java chaincode 编译部署(1.4V)

    前提条件: 构建好了一个拥有四个peer 一个Order 的1.4版本的Fabric网络. 证书通过Cryptogen生成,没有使用CA服务. 开启TLS. 网络中的peer都加入了一个 名为mych ...

  2. Hyperledger Fabric java chaincode 中文乱码问题

    开发java chaincode过程中遇到一个中文乱码的问题.都是官方的demo,请求的sdk是用java写的,部署的chaincode有两种选择(不考虑node),一种go语言写的chaincode ...

  3. Hyperledger Fabric 1.2 --- Chaincode Operator 解读和测试(一)

    前言 本文主要目的是用于整理Hyperledger  Fabric中关于chaincode 管理和操作的内容,作者以release-1.2为范本进行讲解. 主要参考链接: https://hyperl ...

  4. Hyperledger Fabric(4)链码ChainCode

    智能合约,是一个抽象的概念,智能合约的历史可以追溯到 1990s 年代.它是由尼克萨博(Nick Szabo)提出的理念,几乎与互联网同龄. 我们这里所说的智能合约只狭义的指区块链中.它能够部署和运行 ...

  5. Hyperledger Fabric Chaincode for Operators——实操智能合约

    什么是Chaincode(智能合约)? chaincode是一个程序,它是使用Go语言编写的,最终在Java等其他编程语言中实现了指定的接口.chaincode运行在一个被背书peer进程独立出来的安 ...

  6. HyperLedger Fabric ChainCode开发——shim.ChaincodeStubInterface用法

    深蓝前几篇博客讲了Fabric的环境搭建,在环境搭建好后,我们就可以进行Fabric的开发工作了.Fabric的开发主要分成2部分,ChainCode链上代码开发和基于SDK的Application开 ...

  7. Hyperledger Fabric 1.2 --- Chaincode Operator 解读和测试(二)

    本文接上一节是测试部分 搭建一个模拟测试环境 作者将fabric release1.2工程中的 example-e2e进行了改造来进行本次实验: (1)首先我们将examples/e2e_cli/sc ...

  8. 搭建基于hyperledger fabric的联盟社区(七) --升级chaincode

    上个版本的chaincode有很多功能不完备,所以要部署新版本的chaincode.Fabric支持在保留现有状态的前提对chaincode进行升级. 一.新版chaincode 新版本的chainc ...

  9. 搭建基于hyperledger fabric的联盟社区(四) --chaincode开发

    前几章已经分别把三台虚拟机环境和配置文件准备好了,在启动fabric网络之前我们要准备好写好的chaincode.chaincode的开发一般是使用GO或者JAVA,而我选择的是GO语言.先分析一下官 ...

随机推荐

  1. 利用phpStudy 探针 提权网站服务器

    声明: 本教程仅仅是演示管理员安全意识不强,存在弱口令情况.网站被非法入侵的演示,请勿用于恶意用途! 今天看到论坛有人发布了一个通过这phpStudy 探针 关键字搜索检索提权网址服务器,这个挺简单的 ...

  2. sqlserver2008 job设定

    数据同期: update owk ),td.) ,owk.RecordEndTime),td.) from openrowset( 'SQLOLEDB ', '172.17.1.14'; 'read' ...

  3. springmvc xml文件配置中使用系统环境变量

    来源:how to read System environment variable in Spring applicationContext 答案: In order to access syste ...

  4. axios中的qs介绍

    首先qs是一个npm仓库所管理的包,可通过npm install qs命令进行安装. 地址: https://www.npmjs.com/package/qs qs.parse().qs.string ...

  5. 随机森林之oob的计算过程

    随机森林有一个重要的优点就是,没有必要对它进行交叉验证或者用一个独立的测试集来获得误差的一个无偏估计.它可以在内部进行评估,也就是说在生成的过程中就可以对误差建立一个无偏估计. 随机森林在生成每颗决策 ...

  6. Windows Server 2019 SSH Server

    Windows Server 2019 SSH Server   在需要安裝的ws2019开启powershell,执行安装 openssh server 指令 Add-WindowsCapabili ...

  7. 【C/C++开发】【VS开发】win32位与x64位下各类型长度对比

    64 位的优点:64 位的应用程序可以直接访问 4EB 的内存和文件大小最大达到4 EB(2 的 63 次幂):可以访问大型数据库.本文介绍的是64位下C语言开发程序注意事项. 1. 32 位和 64 ...

  8. python常见队列queue分类

    import queue # 1.普通q# 2.先进后出q# 3.优先级q 普通Queue q=queue.Queue(3)q.put(1)q.put(2)q.put(3)print(q.get()) ...

  9. Sliding Puzzle

    On a 2x3 board, there are 5 tiles represented by the integers 1 through 5, and an empty square repre ...

  10. 我的第一个Java博客

    1.2019 11.23 Alone in Beijing;