链码(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. HttpURLConnection提交数据

    使用GET方式向服务器端提交数据 * 原理:把要提交的数据组拼到Url后面 * http协议规定数据长度不超过4kb,IE浏览器超过1kb就会丢弃掉后面的数据 * 缺点:数据不安全 * 优点:代码书写 ...

  2. Mysql - 面试题 获取每个学生的最高成绩

    题目 如下一个表, 三列, 姓名, 课程, 以及成绩, 现在想要得到知道每个学生最高成绩的课程名字. 解题

  3. WPF 绑定集合 根据集合个数改变样式 INotifyCollectionChanged

    问题:当前ListBox Items 绑定 集合数据源ListA时候:ListA集合数据源中存在另外一个集合ListB,当更改或往ListB集合中添加数据的时候,通知改变? 实体类继承 INotify ...

  4. vue-cli的项目中关于axios的全局配置,结合element UI,配置全局loading,header中做token传输

    在src目录中建立plugins文件夹,在文件夹内建立axios.js文件 "use strict"; import Vue from 'vue'; import axios fr ...

  5. Unity小白文——单例的定义

    当类继承与MonoBehaviour时 public class TestSingle : MonoBehaviour { public static TestSingle Instance; voi ...

  6. 欢迎关注微信公众号codefans一起交流技术

  7. 【CSS】如何在一个页面中引入样式css

    CSS(Cascading Style Sheet)又叫层叠样式表.是我们学习前端必不可少的一门语言,学习它其实就是为了学会如何去更改页面标签的样式.目前使用最广的是css3,但同样的,他是从css2 ...

  8. 今天发现一个Window系统服务增删改查神器:NSSM

    官网地址:https://nssm.cc Win10系统下这个:https://nssm.cc/ci/nssm-2.24-101-g897c7ad.zip 官方的帮助,英语的,可以大概看一下: htt ...

  9. C#对IQueryable<T>、IEnumerable<T>的扩展方法

    #region IQueryable<T>的扩展方法 #region 根据第三方条件是否为真是否执行指定条件的查询 /// <summary> /// 根据第三方条件是否为真是 ...

  10. XSS练习平台-XSS Challenges

    XSS Challenges http://xss-quiz.int21h.jp/   XSS基础不好的建议优先查看 关于XSS中使用到的html编码 js编码各种场景用法 http://su.xmd ...