Hyperledger Fabric(5)ChainCode的编写步骤
链码(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.Success或shim.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的编写步骤的更多相关文章
- Hyperledger Fabric java chaincode 编译部署(1.4V)
前提条件: 构建好了一个拥有四个peer 一个Order 的1.4版本的Fabric网络. 证书通过Cryptogen生成,没有使用CA服务. 开启TLS. 网络中的peer都加入了一个 名为mych ...
- Hyperledger Fabric java chaincode 中文乱码问题
开发java chaincode过程中遇到一个中文乱码的问题.都是官方的demo,请求的sdk是用java写的,部署的chaincode有两种选择(不考虑node),一种go语言写的chaincode ...
- Hyperledger Fabric 1.2 --- Chaincode Operator 解读和测试(一)
前言 本文主要目的是用于整理Hyperledger Fabric中关于chaincode 管理和操作的内容,作者以release-1.2为范本进行讲解. 主要参考链接: https://hyperl ...
- Hyperledger Fabric(4)链码ChainCode
智能合约,是一个抽象的概念,智能合约的历史可以追溯到 1990s 年代.它是由尼克萨博(Nick Szabo)提出的理念,几乎与互联网同龄. 我们这里所说的智能合约只狭义的指区块链中.它能够部署和运行 ...
- Hyperledger Fabric Chaincode for Operators——实操智能合约
什么是Chaincode(智能合约)? chaincode是一个程序,它是使用Go语言编写的,最终在Java等其他编程语言中实现了指定的接口.chaincode运行在一个被背书peer进程独立出来的安 ...
- HyperLedger Fabric ChainCode开发——shim.ChaincodeStubInterface用法
深蓝前几篇博客讲了Fabric的环境搭建,在环境搭建好后,我们就可以进行Fabric的开发工作了.Fabric的开发主要分成2部分,ChainCode链上代码开发和基于SDK的Application开 ...
- Hyperledger Fabric 1.2 --- Chaincode Operator 解读和测试(二)
本文接上一节是测试部分 搭建一个模拟测试环境 作者将fabric release1.2工程中的 example-e2e进行了改造来进行本次实验: (1)首先我们将examples/e2e_cli/sc ...
- 搭建基于hyperledger fabric的联盟社区(七) --升级chaincode
上个版本的chaincode有很多功能不完备,所以要部署新版本的chaincode.Fabric支持在保留现有状态的前提对chaincode进行升级. 一.新版chaincode 新版本的chainc ...
- 搭建基于hyperledger fabric的联盟社区(四) --chaincode开发
前几章已经分别把三台虚拟机环境和配置文件准备好了,在启动fabric网络之前我们要准备好写好的chaincode.chaincode的开发一般是使用GO或者JAVA,而我选择的是GO语言.先分析一下官 ...
随机推荐
- WPF 绑定集合 根据集合个数改变样式 INotifyCollectionChanged
问题:当前ListBox Items 绑定 集合数据源ListA时候:ListA集合数据源中存在另外一个集合ListB,当更改或往ListB集合中添加数据的时候,通知改变? 实体类继承 INotify ...
- nginx.conf 配置 (反向代理,负载均衡,fastdfs model)
#user nobody;worker_processes 1; #error_log logs/error.log;#error_log logs/error.log notice;#error_l ...
- linux简单命令8---软件包安装
1:使用yum安装,它不能包查询和包校验.它安装的是RPM格式文件.没有yum文件 ---------------------------------------------------------- ...
- Fidessa
Fidessa这样为券商提供交易系统和与交易所连接的公司被称作Independent Software Vendor, 同类的还有FIS(前SunGuard), Bloomberg(AIM), Tho ...
- unmix和conditional average:消混叠和条件均值
unmix 该程序用来消除“像素混叠”.所谓像素混叠,是值在自然场景的图像中,边缘线成像到cmos的像素上时,某些像素会刚好跨在该边缘线上. 这样的像素特点就是,其R.G.B三色像素梯度值不一致.比如 ...
- MySQL安装Write configuration file 提示:configuration file template my.ini Error code-1
在安装MySQL的时候, 在最后安装时,最后一步出现Write configuration file没成功勾选,并提示:configuration file template D:\mysql\my- ...
- ASP.NET Core 入门笔记4,ASP.NET Core MVC路由入门
敲了一部分,懒得全部敲完,直接复制大佬的博客了,如有侵权,请通知我尽快删除修改 摘抄自https://www.cnblogs.com/ken-io/p/aspnet-core-tutorial-mvc ...
- PJzhang:CVE-2019-14287 sudo权限绕过漏洞复现
猫宁!!! 参考链接:Ms08067实验室公众号 sudo 1.8.28版本之前有漏洞. 更新完kali linux,deepin截图工具失效,只能用自带的,不能划重点. 看一下sudo版本,1.8. ...
- windows 3种方式运行exe文件
1.双击文件运行 2.打开cmd,cd 到要运行的文件目录下,输入文件名或者文件名.exe 3.将文件目录配置到系统环境变量,按windws+R输入文件名或者文件名.exe
- 零零散散的python笔记
Python strip()方法 用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列. #!/usr/bin/python # -*- coding: UTF-8 -*- str = &qu ...