参考:http://hyperledger-fabric.readthedocs.io/en/latest/chaincode4ade.html

chaincode是由go语言写的,实现了定义的接口。其他语言例如JAVA也是支持的。通过application体积的transaction,chaincode可以初始化并管理Ledger状态。

一个chaincode创建的Ledger状态是独立的,不能被其他chaincode直接访问。在合适的许可下,chaincode能够调用在相同网络下的其他chaincode用于访问其Ledger状态。

接下来,我们以chaincode开发者的视野来看看chaincode。下面我们以一个chaincode案例来看看chaincode Shim API的每一个方法。

1.Chaincode API

每一个chaincode需要实现Chaincode接口,其方法是用于响应接收到的transaction。当chaincode接收到instantiate或者upgrade transaction时Init方法被调用了,以便chaincode能够执行任何必要的初始化,包括application state的初始化。当chaincode接收到invoke transaction时调用invoke方法,用于处理transaction proposal。

“shim”API中其他的接口是 ChaincodeStubInterface  用于访问及改变Ledger,以及在chaincode之间调用。

在本教程中,我们将通过实现简单的chaincode应用程序(管理简单的“资产”)来演示这些API的使用。

2.简单的资产chaincode

我们的application是一个基本的样例chaincode,用于在Ledger上创建资产(键值对)。

2.1 选择代码的位置

首选需要确定Go的环境被安装以及被合适的配置。

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

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

接下来,我们创建源码文件

touch sacc.go

2.2 Housekeeping

每一个chaincode都实现了Chaincode接口 <https://github.com/hyperledger/fabric/blob/master/core/chaincode/shim/interfaces.go#L28>,特别是Init以及Invoke函数。因此,我们引入

package main

import (
"fmt" "github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/protos/peer"
)

2.3 初始化chaincode

接下来,我们事先Init函数

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

注:chaincode upgrade也调用这个函数。当一个chaincode要升级时,确保合适修正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) != {
return shim.Error("Incorrect arguments. Expecting a key and a value")
}
}

接下来,由于我们的调用是有效的,我们将会在Ledger上存储初始状态。为了实现状态的存储,我们将会调用ChaincodeStubInterface.PutState方法,并把键值作为参数进行输入。假设一切都工作正常,则会返回一个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) != {
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[], []byte(args[]))
if err != nil {
return shim.Error(fmt.Sprintf("Failed to create asset: %s", args[]))
}
return shim.Success(nil)
}

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

就像上述Init的函数那样,我们需要通过ChaincodeStubInterface获取参数。Invoke函数的参数就是需要调用chaincode应用的名称。在我们的例子中,我们的应用有两个简单的函数set与get,允许asset的值被设定,同时允许获取现在的状态。我们首先调用ChaincodeStubInterface.GetFunctionAndParameters用来获取chaincode应用的函数名称与参数。

// 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 {
// Extract the function and args from the transaction proposal
fn, args := stub.GetFunctionAndParameters() }

接着,我们设置set与get函数名称,并调用这些chaincode应用函数,通过shim返回一个合适的响应。Error函数将会把一个响应序列化成gRPC protobuf消息。

// 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 {
// Extract the function and args from the transaction proposal
fn, args := stub.GetFunctionAndParameters() var result string
var err error
if fn == "set" {
result, err = set(stub, args)
} else {
result, err = get(stub, args)
}
if err != nil {
return shim.Error(err.Error())
} // Return the result as success payload
return shim.Success([]byte(result))
}

2.5 实现chaincode应用

我们的chaincode应用实现了两个函数,能够通过Invoke进行调用。接下来实现这些函数。就像我们上面所提到的,我使用chaincode shim API的ChaincodeStubInterface.PutState与ChaincodeStubInterface.GetState来访问access的状态。

// Set stores the asset (both key and value) on the ledger. If the key exists,
// it will override the value with the new one
func set(stub shim.ChaincodeStubInterface, args []string) (string, error) {
if len(args) != {
return "", fmt.Errorf("Incorrect arguments. Expecting a key and a value")
} err := stub.PutState(args[], []byte(args[]))
if err != nil {
return "", fmt.Errorf("Failed to set asset: %s", args[])
}
return args[], nil
} // Get returns the value of the specified asset key
func get(stub shim.ChaincodeStubInterface, args []string) (string, error) {
if len(args) != {
return "", fmt.Errorf("Incorrect arguments. Expecting a key")
} value, err := stub.GetState(args[])
if err != nil {
return "", fmt.Errorf("Failed to get asset: %s with error: %s", args[], err)
}
if value == nil {
return "", fmt.Errorf("Asset not found: %s", args[])
}
return string(value), nil
}

2.6 合并上述代码

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 {
} // Init is called during chaincode instantiation to initialize any
// data. Note that chaincode upgrade also calls this function to reset
// or to migrate data.
func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {
// Get the args from the transaction proposal
args := stub.GetStringArgs()
if len(args) != {
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[], []byte(args[]))
if err != nil {
return shim.Error(fmt.Sprintf("Failed to create asset: %s", args[]))
}
return shim.Success(nil)
} // 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 {
// Extract the function and args from the transaction proposal
fn, args := stub.GetFunctionAndParameters() var result string
var err error
if fn == "set" {
result, err = set(stub, args)
} else { // assume 'get' even if fn is nil
result, err = get(stub, args)
}
if err != nil {
return shim.Error(err.Error())
} // Return the result as success payload
return shim.Success([]byte(result))
} // Set stores the asset (both key and value) on the ledger. If the key exists,
// it will override the value with the new one
func set(stub shim.ChaincodeStubInterface, args []string) (string, error) {
if len(args) != {
return "", fmt.Errorf("Incorrect arguments. Expecting a key and a value")
} err := stub.PutState(args[], []byte(args[]))
if err != nil {
return "", fmt.Errorf("Failed to set asset: %s", args[])
}
return args[], nil
} // Get returns the value of the specified asset key
func get(stub shim.ChaincodeStubInterface, args []string) (string, error) {
if len(args) != {
return "", fmt.Errorf("Incorrect arguments. Expecting a key")
} value, err := stub.GetState(args[])
if err != nil {
return "", fmt.Errorf("Failed to get asset: %s with error: %s", args[], err)
}
if value == nil {
return "", fmt.Errorf("Asset not found: %s", args[])
}
return string(value), nil
} // main function starts up the chaincode in the container during instantiate
func main() {
if err := shim.Start(new(SimpleAsset)); err != nil {
fmt.Printf("Error starting SimpleAsset chaincode: %s", err)
}
}

2.7 build chaincode

编译chaincode

go get -u --tags nopkcs11 github.com/hyperledger/fabric/core/chaincode/shim
go build --tags nopkcs11

接下来测试chaincode

2.8 使用dev模式测试

一般来说,peer启动并持有chaincode。然而在开发模式下,chaincode由用户build并启动。在快速代码/构建/运行/调试周期周转期间的链码开发阶段,此模式非常有用。

我们通过为利用预先生成的order和channel artifacts来启动一个简单的开发网络的“开发模式”。 因此,用户可以立即编译chaincode和调用函数。

3.安装hyberLedger fabric 样例

请先安装hyberLedger fabric 样例。

进入fabric-samples以及chaincode-docker-devmode目录

cd chaincode-docker-devmode

4.下载docker镜像

我们需要四个Docker镜像用于开发模式允许docker compose script.脚本,如果你已经安装了fabric-samples仓库克隆,并且按照说明下载了platform-specific-binaries,接下来你应该在本地按照Docker镜像。输入docker images命令去展示Docker镜像。应该能看到如下:

docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hyperledger/fabric-tools latest e09f38f8928d hours ago 1.32 GB
hyperledger/fabric-tools x86_64-1.0.-rc1-snapshot-f20846c6 e09f38f8928d hours ago 1.32 GB
hyperledger/fabric-orderer latest 0df93ba35a25 hours ago MB
hyperledger/fabric-orderer x86_64-1.0.-rc1-snapshot-f20846c6 0df93ba35a25 hours ago MB
hyperledger/fabric-peer latest 533aec3f5a01 hours ago MB
hyperledger/fabric-peer x86_64-1.0.-rc1-snapshot-f20846c6 533aec3f5a01 hours ago MB
hyperledger/fabric-ccenv latest 4b70698a71d3 hours ago 1.29 GB
hyperledger/fabric-ccenv x86_64-1.0.-rc1-snapshot-f20846c6 4b70698a71d3 hours ago 1.29 GB

5.启动网络

docker-compose -f docker-compose-simple.yaml up

上述代码启动了包括SingleSampleMSPSolo orderer profile的网络,同时启动开发模式的peer。这个启动了另外两个容器,一个是chaincode的环境以及与chaincode交互的CLI。在CLI容器中进行创建与加入channel的命令,因此我们可以开始chaincode的调用。

6.build与启动chaincode

docker exec -it chaincode bash

进入容器,

root@d2629980e76b:/opt/gopath/src/chaincode#

接下来,编译chaincode

cd sacc
go build

现在运行chaincode:

CORE_PEER_ADDRESS=peer: CORE_CHAINCODE_ID_NAME=mycc: ./sacc

peer启动了chaincode,以及chaincode日志表明peer成功注册了chaincode。该阶段chaincode没有与任何channel产生关联。这在使用实例化命令的后续步骤中完成。

7.使用chaincode

即使现在在--peer-chaincodedev模式下,仍然需要安装chaincode,以便生命周期的chaincode能够正常检查。

我们利用CLI容器去调用这些方法

docker exec -it cli bash
peer chaincode install -p chaincodedev/chaincode/sacc -n mycc -v
peer chaincode instantiate -n mycc -v -c '{"Args":["a","10"]}' -C myc

接下来改变a的值为20.

peer chaincode invoke -n mycc -c '{"Args":["set", "a", "20"]}' -C myc

最后查询

peer chaincode query -n mycc -c '{"Args":["query","a"]}' -C myc

8.测试新的chaincode

这个案例中,我们只实现了sacc。我们可以很轻松的测试其他的chaincode通过吧这些chaincode加入到chaincode子目录下,然后重启网络。此时,他们能够在chaincode容器中被访问。

HyberLedger Fabric学习(3)-chaincode学习(开发者)的更多相关文章

  1. fabric私密数据学习笔记

    fabric私密数据学习笔记 私密数据分为两部分 一个是真正的key,value,它被存在 peer的私密数据库(private state)中. 另一部分为公共数据,它是真实的私密数据key,val ...

  2. 2019最新WEB前端开发小白必看的学习路线(附学习视频教程)

    2019最新WEB前端开发小白必看的学习路线(附学习视频教程).web前端自学之路:史上最全web学习路线,HTML5是万维网的核心语言,标准通用标记语言下的一个应用超文本标记语言(HTML)的第五次 ...

  3. python3.4学习笔记(七) 学习网站博客推荐

    python3.4学习笔记(七) 学习网站博客推荐 深入 Python 3http://sebug.net/paper/books/dive-into-python3/<深入 Python 3& ...

  4. Java全栈学习路线、学习资源和面试题一条龙

    肝了一个月,终于把Java学习路线.面试题资源和电子书资源都整理好了. Java 从基础到微服务的学习路线,其中还包括科班知识.学习建议.后续的学习引导和相应的学习资源(视频.书籍.网站),还整理了J ...

  5. (转)Predictive learning vs. representation learning 预测学习 与 表示学习

    Predictive learning vs. representation learning  预测学习 与 表示学习 When you take a machine learning class, ...

  6. java JDK8 学习笔记——助教学习博客汇总

    java JDK8 学习笔记——助教学习博客汇总 1-6章 (by肖昱) Java学习笔记第一章——Java平台概论 Java学习笔记第二章——从JDK到IDEJava学习笔记第三章——基础语法Jav ...

  7. [未完成]WebService学习第一天学习笔记

    [未完成]WebService学习第一天学习笔记[未完成]WebService学习第一天学习笔记

  8. Asp.net MVC4高级编程学习笔记-视图学习第一课20171009

    首先解释下:本文只是对Asp.net MVC4高级编程这本书学习记录的学习笔记,书本内容感觉挺简单的,但学习容易忘记,因此在边看的同时边作下了笔记,可能其它朋友看的话没有情境和逻辑顺序还请谅解! 一. ...

  9. 最近开始学习Cesium,学习学习。

    最近开始学习Cesium,学习学习.

随机推荐

  1. Java中的注解基础

    一.元注解 元注解的作用就是负责注解其他注解. 1.@Target @Target用来指明注解所修饰的目标,包括packages.types(类.接口.枚举.Annotation类型).类型成员(方法 ...

  2. m_Orchestrate learning system---二十八、字體圖標iconfont到底是什麼

    m_Orchestrate learning system---二十八.字體圖標iconfont到底是什麼 一.总结 一句话总结: 阿里巴巴 图标库 iconfont-阿里巴巴矢量图标库 1.表格的t ...

  3. BooStrap4文档摘录 Utilities

    border:可以用原生css实现效果.❌没看 clearfix, float,  ✅ close icon ✅ colors ✅ display✅  各种显示的格式. embed ✅ <ifr ...

  4. selenium 3.6.0 geckodriver的一次坑

    Traceback (most recent call last):  File "./se3.py", line 16, in <module>    dr=webd ...

  5. HDU 4522 (恶心建图)

    湫湫系列故事——过年回家 Time Limit: 500/200 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)Total ...

  6. 创建对象并生成结果的3个步骤-Chapter 3 P38

    必须完成3个步骤才能创建对象并生成结果: 1 创建对象   namespace LanguageFeatures { public class Product { public int Product ...

  7. IOS-网络(文件上传)

    // // ViewController.m // IOS_0206_文件上传 // // Created by ma c on 16/2/6. // Copyright © 2016年 博文科技. ...

  8. C++设计与声明——让接口容易被正确使用

    一个简答易错的例子 class Date { public Date(int month,int day,int year); } 一年后使用这个接口的时候,写了Date d(15,10,2015)或 ...

  9. WIN8.1 PRO RTM VOL.2013.09.18

    文件名:cn_windows_8_1_pro_vl_x64_dvd_2791218.isoSHA1:61C002551763E22B64EB1BACEFFE83620114C3D6 文件名:cn_wi ...

  10. 『转』Emsisoft Anti-Malware 8刷Key教程 - 文字版

    先分主机和客机,下载好 EAM8安装包 和 30天重置工具EAM Trial Reset 1.1.exe 1. 主机安装 Emsisoft Anti-Malware 8 并激活30天试用版   如果已 ...