Hyperledger Fabric源代码分析 9787111608707

内容简介 这是一本深度解读Hyperledger Fabric架构设计与实现原理的著作,由国内知名区块链公司趣链科技的创始人和核心技术团队成员撰写。 全书的核心内容以Hyperledger Fabric的源代码为切入点,首先从宏观上

924 132 60MB

Chinese Pages 396 [406] Year 2018

Report DMCA / Copyright

DOWNLOAD FILE

Polecaj historie

Hyperledger Fabric源代码分析
 9787111608707

Table of contents :
序一
序二
前言
目录
第1章 准备工作
1.1 Go语言环境配置
1.1.1 Go语言简介
1.1.2 Go安装
1.1.3 Go标准包安装
1.1.4 第三方工具安装
1.1.5 Go环境配置
1.1.6 代码目录结构规划
1.1.7 编译应用
1.1.8 获取远程包
1.1.9 程序的整体结构
1.2 安装Docker
1.2.1 macOS
1.2.2 Ubuntu
1.2.3 Docker的简易使用
1.3 Hyperledger社区介绍
第2章 架构分析
2.1 Fabric整体架构
2.1.1 概述
2.1.2 系统架构
2.1.3 交易背书的基本工作流程
2.1.4 背书策略
2.1.5 证实账本和节点账本检查
2.2 Fabric交易流程
2.3 Fabric整体项目结构介绍
2.3.1 Fabric项目结构
2.3.2 Fabric源码中相关缩写的含义
第3章 源码分析
3.1 Logging日志模块浅析
3.1.1 go-logging简介
3.1.2 flogging
3.1.3 init函数、MustGetLogger函数与其他函数
3.2 Error错误机制设计
3.2.1 总体概览
3.2.2 使用说明
3.2.3 显示错误消息
3.2.4 错误处理的一般准则
3.3 Config配置模块的设计
3.3.1 viper简介
3.3.2 安全文件配置
3.3.3 命令选项配置
3.3.4 环境变量配置
3.4 grpc服务
3.4.1 grpc用法的Demo
3.4.2 Fabric中的grpc服务接口和实例
第4章 peer的设计与实现
4.1 CommandLine解析
4.1.1 peer目录结构
4.1.2 第三方包
4.1.3 peer命令结构解析
4.1.4 以node为例进行子命令结构解析
4.1.5 peer命令结构
4.2 Admin及Endorser服务的实现
4.2.1 Admin
4.2.2 Endorser
4.2.3 频道中的策略检查器
4.3 Committer的机制
4.3.1 committer.go 分析
4.3.2 committer_impl.go 分析
4.3.3 validator.go 分析
4.3.4 vscc_validator.go分析
第5章 order的设计与实现
5.1 orderer内部机制窥探
5.1.1 kingpin
5.1.2 模块
5.1.3 配置
5.1.4 模块的初始化
5.2 kafka排序服务机制讲解
5.3 orderer在Fabric中的交互流程
5.3.1 建立连接
5.3.2 Broadcast
5.3.3 orderer
5.3.4 Deliver
第6章 chaincode的设计与实现
6.1 chaincode生命周期管理
6.1.1 打包
6.1.2 安装chaincode
6.1.3 实例化chaincode
6.1.4 升级chaincode
6.1.5 停止与启动
6.1.6 CLI
6.2 chaincode原理浅析
6.2.1 什么是chaincode
6.2.2 Chaincode Support服务
6.2.3 FSM
6.2.4 Register
6.2.5 Handler
6.2.6 processStream
6.2.7 HandleMessage
6.2.8 serialSend或serialSendAsync
6.2.9 系统chaincode
6.3 chaincode数据结构分析
6.3.1 chaincode元数据
6.3.2 chaincode的元工具
6.4 SystemChaincode讲解
6.4.1 SystemChaincode
6.4.2 预定义和注册
6.5 CSCC分析
6.5.1 结构体
6.5.2 函数
6.6 ESCC分析
6.6.1 结构体
6.6.2 Init函数
6.7 LSCC分析
6.7.1 结构体和接口
6.7.2 函数操作
6.7.3 安装、部署和升级
6.7.4 chaincode stub 接口实现
6.8 QSCC分析
6.8.1 结构体
6.8.2 函数操作
6.8.3 路由规则
6.9 VSCC分析
6.9.1 结构体
6.9.2 函数
6.10 SystemChaincode的注册和实例化
6.10.1 概述
6.10.2 安装
6.10.3 部署
6.10.4 Launch
6.10.5 Execute
6.10.6 部署后状态
6.11 ApplicationChaincode的部署
6.11.1 概述
6.11.2 生成签名申请包
6.11.3 处理安装申请
6.11.4 执行申请
6.11.5 Launch
6.11.6 Execute
6.11.7 一路返回
6.11.8 安装后的状态
6.12 ApplicationChaincode的实例化
6.12.1 概述
6.12.2 起点
6.12.3 部署
6.12.4 广播
6.12.5 部署后的状态
6.13 chaincode操作步骤
6.13.1 选择一个代码存放位置
6.13.2 内务处理
6.13.3 初始化chaincode
6.13.4 调用chaincode
6.13.5 实现chaincode应用
6.13.6 整合全部代码
6.13.7 编译chaincode
6.13.8 在开发者模式下测试
6.13.9 安装Hyperledger Fabric样例
6.13.10 下载Docker镜像
6.13.11 1号终端
6.13.12 2号终端
6.13.13 3号终端
6.13.14 测试新的chaincode
第7章 MSP成员服务提供者
7.1 MSP的设计思路
7.1.1 MSP配置
7.1.2 如何生成MSP证书和它们的签名匙
7.1.3 MSP setup on the peer & orderer side
7.1.4 Channel MSP setup
7.1.5 最佳实践
7.2 MSP实现剖析
7.2.1 目录结构
7.2.2 MSP配置
第8章 Gossip节点间的流言蜚语
8.1 Gossip协议原理解析
8.1.1 Gossip协议(Gossip protocol)
8.1.2 Gossip消息传输(Gossip messaging)
8.2 Gossip之服务组件
8.2.1 protos/gossip分析
8.2.2 Gossip服务组件
8.2.3 gossip消息发送方式详解
8.3 Gossip之服务初始化
8.3.1 gossipSvc 组件
8.3.2 chains组件
8.3.3 leaderElection组件
8.3.4 gossip服务的停止
8.4 Gossip之消息广播
8.4.1 gossip服务消息的散播过程
8.4.2 消息从何而来
8.4.3 消息如何散播
8.4.4 消息去往何方
8.5 channel通道的设计与实现
8.5.1 概述
8.5.2 配置文件
8.5.3 命令
8.6 事件机制
8.6.1 Fabric中Event相关实现
8.6.2 events/producer
8.6.3 Go SDK中Event相关实现
第9章 BCCSP加密服务提供者的设计与实现
9.1 密码学相关知识介绍
9.1.1 安全基础
9.1.2 加密基础
9.1.3 哈希函数
9.1.4 共享密钥加密
9.1.5 公钥加密
9.1.6 混合加密
9.1.7 消息验证码
9.1.8 数字签名
9.1.9 数字证书
9.2 BCCSP概要
9.2.1 BCCSP简介
9.2.2 陷阱函数
9.2.3 为什么要使用ECDSA
9.2.4 生成签名
9.2.5 验证签名
9.3 BCCSP源码剖析
9.3.1 BCCSP服务结构
9.3.2 BCCSP中的接口和选项
9.3.3 SW实现方式
9.3.4 pkcs11实现方式
第10章 Fabric CA架构设计与讲解
10.1 Fabric CA用户指南
10.2 Fabric-CA-Server
10.2.1 初始化服务端
10.2.2 算法和密钥长度
10.2.3 启动服务端
10.2.4 配置数据库
10.2.5 PostgreSQL
10.2.6 PostgreSQL SSL配置
10.2.7 MySQL
10.2.8 MySQL SSL配置
10.2.9 配置LDAP
10.2.10 构建一个集群
10.2.11 构建多个CA
10.2.12 登录一个中间CA
10.2.13 升级服务端
10.2.14 升级一个集群
10.3 fabric-ca-client
10.3.1 登录启动用户
10.3.2 注册一个新身份
10.3.3 登录一个节点
10.3.4 从另一个Fabric CA服务器获得CA证书链
10.3.5 重新登录一个身份
10.3.6 撤销一个证书或身份
10.3.7 生成一个CRL
10.3.8 启用TLS
10.3.9 基于属性的访问控制
10.3.10 动态更新服务器配置
10.3.11 联系特定的CA实例
10.4 HSM
第11章 账本机制的设计与实现
11.1 Ledger架构概述
11.1.1 总览
11.1.2 ledger部分摘要
11.2 Ledger之Block-Storage
11.2.1 peer节点中的leveldb
11.2.2 peer节点中的账本
11.2.3 创建
11.2.4 使用
11.2.5 idStore
11.2.6 存储账本ID
11.2.7 ConstructionFlag
11.2.8 账本恢复
11.2.9 BlockStore
11.3 Ledger之VersionedDB
11.3.1 peer节点使用VersionedDB
11.3.2 交易模拟器/交易查询器
11.3.3 重启恢复
11.4 Ledger之HistoryDB
11.4.1 历史查询器
11.4.2 使用
第12章 chaincode智能合约案例分析
12.1 encc_example
12.1.1 chaincode代码分析
12.1.2 使用EncCC
12.2 eventsender
12.3 example
12.4 example
12.5 example
12.6 example
12.7 example
12.8 invokereturnsvalue
12.9 map
12.10 marbles
12.11 passthru
12.12 sleeper
第13章 Fabric-samples项目分析与实践
13.1 Fabric-samples项目结构
13.2 First-network
13.2.1 安装预置环境
13.2.2 想要现在运行吗?
13.2.3 生成网络神器
13.2.4 启动网络
13.2.5 关闭网络
13.2.6 加密生成器
13.2.7 配置交易生成器
13.2.8 运行工具
13.2.9 启动网络
13.2.10 了解Docker Compose技术
13.2.11 使用CouchDB
13.2.12 关于数据持久化的提示
13.2.13 故障排除
13.3 basic-network
13.4 Fabcar
13.4.1 编写第一个应用
13.4.2 下载测试网络(Getting a Test Network)
13.4.3 应用程序如何与网络进行交互
13.4.4 查询账本
13.4.5 更新账本
13.5 Balance transfer
13.5.1 预置环境
13.5.2 工件
13.5.3 运行示例程序
13.5.4 示例—REST APIs请求
13.6 Hyperledger Fabric CA 示例
13.6.1 运行这个示例
13.6.2 了解这个例子
13.7 高性能网络
13.7.1 用例
13.7.2 如何使用
第14章 部署教程
14.1 下载部署环境
14.2 编译peer、orderer、configtxgen等程序
14.3 部署
14.4 Crypto Generator
14.4.1 crypto-config.yaml
14.4.2 crypto-config文件夹
14.5 Configuration Transaction Generator
14.6 networkUp-启动Fabric网络
14.7 运行容器+区域链操作
附录 专业术语

Citation preview

仅供非商业用途或交流学习使

..

, 帽 ’-

趣链科技创始人员核心团队撰写

言如画

Hype

j 原代码角度剖析 从



「 ledg



「 ic 项目的整体架向设计, Fab

以及各核心模块的实现原理

I 王E

TH E SO UR CE CO DE AN AL YS IS OF HY PE RL ED GE R FA BR IC

F a b r ic yperldg

源代码分析与深入解读 蔡亮梁秀波宣章炯

'-

( J .户,-

••



1 I

\)/

仅供~

甘£ ~ali

旦 (;)

, , 商业

m 途或交流学习佼

j哥

0



仅供非 、2

这是-本深度解

i卖

计与实现原理的著作

Hype

「 ledg

「 Fab

「 ic 架构设

, 由国内知名区块链公司趣科

技的创始人和核心术团队成员撰写



全书的核心内窑以

Hype

代码为切入点

「 ledg

「 Fab

「 ic 的源

Hype

「 ledg

, 首先从宏观上分析了 Fab

「 ic 项目的整体架构与设计

分析了

,然 Hype

「 ledg

实现原理

「 Fab

。 此外



后深入



j 原代码详细

「 ic 各个重要模块的设计与

3 为了兼顾没奇区块链开发基础的读

3 书中还加入了

Hype

「 ledg

「 Fab

「 Fab

「 ic 设计机制的基础上

「 ic

开发环境搔

建、综合案例项目部署等实战性内窑,可使读者能 在深入理解

H ype

快速动手实践

「 ledg



全书共

14





1 逻辑上分为两大部

一部分:源码

分析(第 第

Fab

2

「 ic

2 ~才章)

章首先从宏观的角度解

i卖

的整

Hype

「 ledg



体架构、项目的结,以及交易流程这

为后面的源码分析打下基础; 第

3

章分析了

Login

处理框架、

Config

日志模块、

配置模块、

E 「 O 「错误

GRPC

的源码,对理解后续

服务

4

个模块

;原码有帮助 第

4 ~ 1

章深入纤细地分析了

Chaincode



CA

Pe MSP



Gosip



町、。「

d 町、

BCSP



、账本机制等节点和功能的设计与实现

Fab

「 1c­



分内窑能让读者全面、透彻了解整个

这部

Hyperldg Fab

「 ic

的运作机制

1 章及第 第

1 章主要是为开发

用做准备

12 Hype

3 Docke





第二部分.开发实战(

讲解



Go

~ 14

「 ledg

章) 「 Fab

语言开发环境的准备,以及

「 ic

「环境的准备; 第

12

完整的

~ 14

H ype

部署方法

章分别讲解了一个智能合约的案例、 「 ledg

「 Fab

「 ic

I 页目案例

3

以及顶目的



实战部分不仅能提升读者的动手践力,而且 还能辅助他们更好地理解源码分析的内窑,使论和 实践完美融合到一起。

rJ \

2 军简介

'-飞 、_.

/ 仅供非商业用途或交流学习使



nl i 业用途或交流学习使





lJt

l~r

!)(供

Wr 商业

/l]ij)g

主交流节习俭用



商业用途或交流于斗佼川

仅供非商业用途或交流’严斗{川

f)( 供

lc1i'HV.J )主必交流在习使用



Hyperledger Fabric 源代码分析与深入解读 蔡亮梁秀波宣章炯

版一阶

业而

工商

m

仅供非商业用途或交流学习使

械-

nv

机-m

?)



•..-'-

仅供非商业用途或交

0



学习使用

仅供

·-· )数据 CIP 图书在版编自(

北京:机械工业 .一

源代码分析与深入解读/蔡亮,梁秀波宣章炯著

Hyperledger Fabric 2018.9 出版社, (区块链技术丛书)

H…

I.

-7

一 7-1608 978

ISBN

CIP 中国版本图书馆

Hyperledger

- 研究

. 电子商务-支付方式

源代码分析与深入解读 Fabric

2 出版发行.机械工业社(北京市西城区百万庄大街

100037) '

号邮政编码

· 李秋荣 责任校对

责任编辑·张锡鹏 186m 开本:

ISBN 978-7-111-60870 7 书号:

1 版第

9 月第

25 印张:

x 240mm 1/16

年 2018

次: 版

刷·北京市荣盛彩色印有限公司 印

IV. F713.361.3



2 1 0324 )第

2018 数据核字(

I ·

③宣 ②梁…

①蔡… I.

元 89.0

定价

凡购本书,如有缺页、倒脱由社发行部调换

R~ 客 购书热线:(

热线(

010) 88379426 88361066 010) 68326294 88379649 68995259

版权所有·侵必究 封底无防伪标均为盗版 本书法律顾问北京大成师邬务所脱光/邹陇东

'-

( J .,-「

oo

飞 I I

\)/

仅供非商业用途或交流学习佼

010) 88379604

: ( 投稿热线 读者信箱:

hz

it @hz

b o

k.

com

l 次印刷

il ' Rli

业川

j 主必交流学习使



仅供非商业用途或交流学习使

•..-'-

?)

F 二 , ~cev



以企业级联盟区块链技术引领我国新一代信息的突破式发展 区块链技术及其理念被认为是近年来最具颠覆性和革命的创新之一,已在金

融、贸易物流征信公益联网

、 重视



美国、俄罗斯英



共享经济等诸多领域崭露头角,被世界各国高度

德国等相继把区块链上升为家战略



未来,区块链技术

及其理念将对社会生活的各个不同领域产重要影响,凡是涉数据共享、信息可和价 值传递的场景都离不开区块链技术应用



可以说,区块链技术及其理念作为信息共享和

信任协作的基础,将重构人类社会未来生产关系通过与工智能、云计算大数据和 物联网

等新技术理念的结合,将极大



提高社会生产力



区块链将成为未来数字化社会的底层基础设施,从技术面上各行业持续稳

定发展提供信任支撑,其价值主要体现在这些方面

: ( 1 )减少中



环节,实现



清算结

算,通过智能合约提高业务的自动化程度从而为各行带来效率升;(

2 )实现

数据的共享与标准化对接,基于字资产多行业流通实现价值网络自繁殖从而降低 业

务的拓展成本;(

3 )基于历史数据不可篡改

强监管机构的能力;(

、 4 )该技术的多方可信合作和公平激励机制将成为未来分布式商

业的

发展基石,为各行业创造更多的合作机会



在区块链技术快速发展的近十年中,产生了 代

全流程可追溯和智能合约的规则束增



三 的加

次重要的技术演进

: ( 1 )以比特币为

密数字货币应用将区块链技术引入了人们的视野;(

2 )以太坊为代表的可编

程区块链平台为技术应用于各个领域提供了可能;(

3 )以 Hyperchain

为代表的企业级区块链平台使得技术能够



Hyperledger Fabric

Hyper

led

ger

Fa

正应用于商业环境



为讲解对象,其源代码进行了细致分析和深入读以期帮助者更

好地理解和使用企业级的区块链底层平台技术



放的、标准化企业级

Hyperldg

巳 r Fabric



L inux

基金会引导

的开源

联盟区块链

项目,其



心目标

、 支持商业应用的分布式账本框架与基础代码,具有高效共识、

是建立开

智能合约、多级加密权限控制隐私保护等特性

仅供才



l0i

业用途或交流学习佼

相较于比特币和以太坊,

Hyperldg

本书以

bric



仅供非商业用途或交流学习佼

•..-'-

?)

IV

Fabric

的核心优势有:(

1 )实现模块化和可插拔,更能适应

复杂

通道特性保障数据安全,不同之间实现隔离;(

的商业应用

要求

;(

2 )多

3 )实现证书的强化管理,提供 单独的

Fabr

ic

CA

项目;(

4 )具有较好的可扩展性,解搁了原子排序与



( 5 )可根据负载进行灵活部署,将交易处理节点逻辑简化为背书和确认



复杂处理环节

; 。

本书的撰写团队来自于浙江大学区块链研究中心和杭州

| 趣链科技有限公司,具丰富

的区块链底层核心技术研究和上商业应用开发经验

。 国产自主可控的联盟区块链平台 Hyperchain

建设银行、中国联

Gogle

目前已在中国工商银行、农业 生了强烈反

本书对

Hyperldg

由杭州趣链科技有限公司研发的

、美国道富银行葡萄牙商业等数十家内外大型公司

和机构得到了落地应用,在业内产 Fabric-smple



Fabric





架构及其设计模式进行分析,深入解读源代码并结合

项目介绍相关实践技术



提供参考,具有很好的理论和实践价值

相信可以给区块链技术爱好者和关行业从人员

。 陈纯

中国工程院



曾任浙江大学软件院长和计算机研究所

仅供才

l0ni

业用途或交流学习使

士,浙江大学计算机科与技术院教授

IJ

\

仅供非商业用途

!lt

· .· ~ ·~

Fi



ve-a

/ 序

去除炒作泡沫,回归技术本质 区块链的多中心、自动化和可信任理念与特性使得它成为未来息社会共 享、业务协作价值传输的底层技术支撑

。 理念的探索应用,以金融、证券



开始应用区块链



保险



近年来,随着区块链技术的飞速发展和

物联网



跨境贸易、能源等为代表的很多行业都

近几年新注册成立的、业务与区块链有关公司达数千家,专利

申请量由两年前的几百件猛增至五千余,社会对区块链技术人才需求非常大浙江

学等知名高校率先开设了区块链课程,技术和理念深入到社会生活的方面已成 为大势所趋



随着人们对区块链技术和理念认识的深入,其去除泡沫、回归本质趋势非

常明显,人们讨论的热点逐渐从如何炒币赚钱转向了应用区块链来推动各领域产业 升级与变革,区块链的底层核心技术研究和上应用探索成为了产业发展重点



区块链本质上是提供信任保障的技术,可能发展为未来互联网最重要基础设施之一



若能构建以区块链技术为基础的价值传输网络,将深刻地改变未来信息社会形态



以区

块链为底层技术支撑的加密数字货币应用给传统金融体系带来了一定挑战,各国政府和

央行都对加密数字货币应用保持了切的关注



在区块链技术去货币化的探索阶段,业界

普遍认识到了该技术所蕴含的巨大价值,通过在区块链上构建可重用、模化自动 智能合约,许多创新的应用场景不断涌现例如征信管理、跨境贸易资源共享和版权 享等





Hyperldg

巳 r Fabric



术资源消耗严重、交易性能低下

Hyperchain

为代表的企业级区块链平台克服了传统技



缺乏隐私保护等问题,提供了图灵完备的智能合约支持

使得开发者可以更便捷地和部署各领域的区块链商业应用,大促进了基于联盟

区块链商业形态的蓬勃发展

。 Hyperldg



Linux

基金会于

2015

项目,标是使得区块链技术在加密数

年发起的推进区块链数字技术和交易验证开源 字货

初,就有

IBM

、摩根大通思科



Intel

币之外也可广泛应用于其他场景



等科技

和金融巨头加入

重要的子项目之一,它通过支持插件组模块



..-

·』

)

、 -、

.--

化架构,实现了完备的权限管理、创新

I

/

仅供非商业用途

DJi.

交流

学习使



Fabric



Hyperldg

项目成立之 最

交流学习佼用

VI

的一致性算法等相关工作,对区块链技术发展产生了重要影响

Hyperledger Fabric 显



本书对

开发部署区块链应用的者而



Hyperldg

Fabric

对于国内众多想利用

,相关的中文专业资料过于稀少和浅

开发环境的搭建与部署进行了详细地介绍,并对其技术架构

和源码进行了深入地分析,为相关开发人员提供及时的技术参考

。 本书的作者团队来自浙江大学区块链研究中心和杭州趣科技有限公司,在前

沿技术研究和产业化应用方面积累丰富



浙江大学区块链研究中心是专注于领域的

浙江大学校级研究机构,其目标是攻克区块链的底层技术难关有效推动我国自主、安全

、 可控区块链技术的快速发展和产业化推广



杭州趣链科技有限公司是近年来成长极为迅速

的区块链平台和应用解决方案提供商,其研发底层

Hyperchian

商业公司和机构的生

产 论分析和实践应用方面都颇具特色

环境中投入使用

。 。

的技术内涵基础上,有效提升自己在区块链方面开发能力

已在众多大型

深厚的理论功底和扎实践经验使得本书在

相信本书可以在帮助读者深刻理解

Hyperldg

Fabric



Julian Gordon Hyper ledgr

IJ

\



、 -、 ..-

·』

)

.--

I

/

亚太区副总裁

Y 礼, tn



rtiumal

田叽阳刚抽回翩.

ontwdifle

p 叫·南位回'"阳”相田由斟捕

1. 2.

。,

to the Hyperledger Wlkll

。 me

ofbl

帆阳创’咽回缸阻

•ondl

何.

nt



四”

:l= 3



cid.Name ==

.,这

&&



中定义





CIS

spec,

( b)

127

chaincode.

"lscc"

len (cis.

&&

步只有部署、升级

的交

易才会进入

core/haindx.g

args

err = createCIS(cccid.Name,

,其新生成的

用了

CIS

Execut(

和第

2 步从

pro

ctxt, cccid, sp 巳 c ),类似

制和道路来进行

exampl02 会稍有不同

中抽取出的



的安装



sec

CIS

)根据传入的参数新



AC

建了

在内容上

是完全一致

也就此进入了





似于

sec





接着就调

署所述

的机

只不过,传人数据所承载的任务同执行方向也



6) Execute ( ctxt,

Launch (.)和 第

spec )仍依次执行

ccc id,

theCaincodSupr

5 步生成的

CIS

TION

.

t he Cha i 口 c odeSupport.Execute( . .. ),但这里的

,因此

ctyp

的值为

ChaincodeMsg

类型

的消息

6.11.5

用的

,进而生成的供

cMsg



ChaincodeMsg

_ TRANSAC

Launch AC

CanoiclNme

()

等到的

安装

的情况下执行不了

canNme

太久, = lsc:

1 . 0.



此值

c anNme

:= cccid . Get

会使程序进入

if

chainodeSuprt.HsBL(Nm

chrte,

);进入

进而入

if

6.11.6

chrte

. handler.isRug

()分

为具体的执行

1 )在

而返回

选择分

支,



函数,负责交易的具体模拟执行并返回响应

Execut

次得到



(.)函数中,

Jsc:l

.0

canNme

,然后通过

: = cccid. GetCanonicalName ()再

chrte,

ok

sBenLauchd(Nm

) ,获取了

selct

帽 case 2 )在

等待

(msg,

: = cha inc ode Support . chaincodeHa

LSC

sendExecuteMessage ( . .. )开始使用该



Serv

Handler

chrte.andl

以触发状态机进入运行,最后



... )中,通过调用

true), ServHandl 一个状态



msg

发给自身的

handler.tigNxS

handler.xtS

通道以触发

ServHandl



3) ServHandl



,先交给

procesStam

()收到来自

handler.HMsg(i ler.

,在

ServHandl

sendExcutMag(

的状态机进入下

hand



ok

ok =

Execute

Execut

msg





Launch (.)函数在



spec

_TRANSCIO

theChaincodeSupport.Execute ( .)使

然后

〈·



5) ExecuteChaincode ( ... )函数,在

道的

计与实现

handler.xtS

)处理,

serialSndAyc(,

仅供

erc

二 I 二商业用途或交流学习使



ServHandl ) 给

LSC



状态机无任何 Shim

Handler

发送

变 msg

化, 。

•!•

128

Hyperledger Fabri

4 ) LSC



Mesag(in

c 源代码分析与深入解读

ShimHandler



)处理,触发

chatWiPer

befor

()收到

Transctio



handleTransaction ( ... )函数

msg

件函数

,交出

,该事

件函数主要调用同文中的



5 ) handleTransaction ( ... )函数主要做的就是根据 成并初始化



调用

LSC



6 )在 Args



ChaincodeSt

In

LSC



巾,对

voke

()



()中,

case

-到 7 )在

” instal

巳 xample02

functio

的值是” case

工 NS

。 和

ls

examp

TAL



CDS

,执行安装

... )中,首先 中

1 『,

即 然后

(path,

中,



)先检

064

至第

)将

6 步

CDS



exampl02



if 检





查之

后,先

path

口 ame,



Execut

收到

(.)函数

可用



buf

最后

6.11.7 当交易模

件函数

,井

执行

写入

) 整

path

指定的地方



中的

赋值为

handler.tigN

之后就

_ COMPLETD



(.)函数成功返回

客户端

再无其

ShimHandler

只将 他动作或

变化

消息,通知仍处于等待之中

Execut



至此,

. .. )

defr

S 巳 rveHandl

ChaincodeMsg

仅供非商业用途或交流学习使

fmt.

nextSaMsg

发送给

完之后,执行结果就会

:=

handleTrsctio(



一路返回 拟执行



ioutl.WreF



,然后等待结束

Version

cversion

),并将该消息发送给自己的状态机 消息



。 事

的消息

xtSae(nMsg,d

Name

exampl02.nothrvsi

路返回

巳 r 的

( core/ 。

下放入名为

中成员 一

类型

11 ) ServHandl

函数,依

C DSPackge le02

c

名是否

由此,开始

建一个

)返回,继续向下执行将

OMPLETD



源码写入文件系统

Packge

handler.cIvok(stub

ChaincodeMessage _C

仍是被

: = ccprovider. Get CC

examp

此文件

ShimHandl

ChaincodeMessage_COMPLETD



CDS LSC

CDS

其次,简单验证

一系



。 回

工 i cy

查要安装

,不过此时的

, err



的安装申请执行完毕 )一路返

string(args

函数来检

)之后调用

过的

chainodelstP

ccpack .buf,

CDS

inc ode I 口 stalPh,

os.Sta(ph

exampl02 10

定义)

cha



code

depSc

Marshl

. . . )中, /% s . 毛 S

chain

cpak 被

PutChaincodeTFS(



:=



exampl02

合出要写入的路径, 的文件

的 的

()将

S

creatPopsl

functio

exampl02

cpak.PutChinodeTFS

("去

.CS. I 口 put.

仨 Po

]取出来的就是

/ cdspakge.o

Sprintf

仨 e ()中,

Chanel

)会根据

9 )在

vol



CIS

lsc.poiyChekr

l(

调用

In

:分 支中,

excutinsa

comn/prvide

stub) 是

utils.go

cc. executeinstall (stub,

Packge(byts 最后,

因此在



。 /pro

门使用了检测未指定

l e02

8 )在



msg

handler.cIvok(

: = stub. GetArgs ()获取到的

cinp

:= args[l 。

收到的

instal

depSc

过的 stub

安装 ls

”·中的

NoChannel ( ... ) 首先专



args

Handler

,然后

进行

prots/ui

FromCDS ()中的

Marshl

C haincodeStub

exampl02

这个数组的值来自于

[OJ )得

Shim

应图中的

方法对

Invoke

handler.

返回







I ) Execu te (.)函数结束之后,就此 endors

. go



中的 calChinode(

6.14 ca lChaincode(

2 )继

节)第

4 步的(

...)也就此结束

续返 回到

Pro ces

ces Pr

sPr

opsal

opsal

Endorse

户端

与实

返回到



cor

步(



e /en

c )中的

进入代码中的

129

dorse /

instal 申

请不会执

if

if

分支 。

txsim ! = nil 分支,



(),将进入

客户端

因 此继

! = nil

res

的应答消息

续向下走,进人

分支,但无法进入

p Re

if

s p 赋值, 最

if

后将

chainID =’ pRes 返回 给

1 『分

E ndorse 客



4 ) exampl02 的

为安

instal

装申请的起点, per

( ... )中的

/ chainode

cf.EndorseC

/ inst

lient.P

户端,收到服务发来的消息在返回后

6.11.8 在

。 至

此, 整



a l. g o 的 chainodelst(

... )所调用

rocesPpal

instal

Inst a ll ( ... )结束

()即是

E ndorse

(.)随之结束,进而导致

examp le02



chainode

的安装全部结束 。

安装后的状态

per 节点的

c h a incod

文件,该即为

elnsta

examp

l!P

l e02

at h 目录下,会有一个名为

exa

源码压缩包 。

为链码对应的可执行文件源

用于 实

mple02.anothrvsi 的

例化的时候创建相应链码容器,其中



AplicatonChde

6.12

的实例化

链码安装 之后

需要 进行 实

并启动链码容器的过程

例化才能够进行后续调用,实过程为创建链码的镜像



6.12.1 概述

peer chaincode instae instae.go 中

node star 、

p e



,这

已 根据实例

经建立



一 句实际的

也已 则

的起点

P 巳 er

。 per

经安装

,从



-o

1 { 11 Arg s 11 : [ 1 『 int1

a1

,”



要注意的 p e er



/

点是,这个命令在 per

chaincode instal

节点的基本模块(包括

e_

-v anotherversion



/ chainode

sec

命 令依次执行完毕

)都已初始化完毕,



insta

instae

另外

channel join

之时,

, AC 化的 原

per



instae

chanel

令执行部署命,定义在

也是部

r channel creat

之后执行的,即

用上)

一直





()

res . Status >= shim. ERO 支对要返回给

的设计

。 ,不

P ro

3 )继续

路返回,

b ),由于这

simulateProp

因此也是直接返回至

chainode

... ) , chaincode.ExecuteChaincode( . . . )

执行完毕,申请的( 行,因此

6 章

令 :

tes.go

per

和 官方文档中提取整合了(尽量能用的

flag

chaincode instantiate

,”

orderer. examp l e. com: 7050 10

仅供非商业用途或交流学习使

。”, lb

, ” 20 。”]}『-

-n example02

C test chain P

"OR



(10rg

工 MSP.

-c

仅供非商业用途或交流学习使

•!•

130

Hyperledger Fabric j 原代码台析



member t ,’ Org2 MS P . member 『) anoth

巳凹

ersion,

要 部署

-o

的链是

指定连接的

or

teschain

in sta

der

flag

- I 、 化

署是存储在

b 两个账户

节点通过

g rpc exampl02

的 lop





化 der



,最后将 进

起点位置在

mple02

行背书

cod

通过

grpc

执行部

部署

的步



巳 Packge







分繁杂

,下面

nil

o

将进行详

中的

2)

) ,这点将

teschain

,不



insta

l 命令



执行的线路类似,对 )在部

再为空,



/ utils

署成

LSC





exampl02



功的

” upgrade

Invoke

”:

所进入的分支

dors

e r 服务端在

t 上

c ore/

来自客户端的

Channel Id

n tex

ctx

t , 一

ndorse

ctx

l 时

下文



安装在

分支(

cas



te s tchain

,以

至于之

ct

ionB

。 ( b )调

y ID



(txid

)对

txi

txsim,

仅供

er r

· 1 二商





i 主或交流学习使用



=

”中执



务端

if

,



...)

处在于



chain

:= 会

的账 )

的唯

本对象 一



ID

chainID !=川分支都 ID

=

SignedPropsal

Procespal(

teschain

(交易

err

h ist oryQuery Executor,



获取 d

” d 巳 ploy

... )所做的 之



),



teschain

e 的

Procespal(



: = peer. Get Ledger (cha i 口 ID

装好

中的

instal

获取的值为



creatPops!FmCDS()

E ndorse

/ endors.g

所描述的那样,但不同于

C DS



起发给

SignedPropsal



exampl02 的

case



关于

/ proutils.g

l ,这次进入的是

直接影

Co

insta



LSC,



prots

个之后一直在用的

情依旧如

为了避免重复部署)

3 )获取

cf.BroadstClienS(v

细讲解

值为 in sta

gh

3 ) En

lgr.GetTans



per

( .. . ),主

2 ) cf. EndorserClient. Process Proposal ( ... )将组

lgr

容器并与

chainodeDply



chainID

,不过同于

fa lthrou

收到

exampl02



的值依旧来自于 c cinp





据封装成

. go

env

CIS.nputArgs





exampl02

1 ) instantiate ()是部署的起点,中途所组装



进行通信

部署

6.12.3



,这里就

署交易

的部署交易,启动

巳 /instae

点广播部署结果



instal



env

前提下,向各个节

中的

相对于

, 然后将签名、读写集等部署产生的数

进行部署,并返回结果



-C 。

1 ) LSC

1 ) env, err := instantiate( cmd, cf)



指定





节点交由其处理

p 巳 e r/ chain

可以实现两件事:

Cod



起点

6.12 .2

exa

- P 指定策略

- v 指定版本

- C

初始化的状态写人自己

E SC or



2,

Handler 执行自

信进行初始

巳 ,发送到

e re.xamplco:705,

容器中的,启动两端

2 ) exampl

读写集,使用

ord

ample0

。 :

写集

ex

-P 、 - E 、 - V 、

最终要做三件事情,涉及条链码

的源码放入自己

Enve

- c , -n, - v 、

a 、

Docker

in s tanie



服务实例的端点是



- c , 要求部署的时候初始 的部

AC

参数

能够识别的

AC

- n 指定部署的

,心指定执行的函数和

ntiae

指定了



深入解读

性进行检

进入:(

chdr.

lgr



a) 并调用

查(这里是

e.getTxSimulator(chainID)

e. getHistoryQueryExecutor

仅供#商业用途或交流学习使



(chainID

);,分别获取

teschain

historyQueExc ,



ex

ample02



4 ) 在

e .si



部署进行背书

if . .分支

&&

Cha

incode

所述之处在于,执行 分

if

Execut( instal

LSC



取得 是

deploy



到的

e . ca

支,



txsim

!=

会进入最后的

if

l

cid.

nil



支,将第

3 步(

CIS

b)

的对象,与

ctx

... )函数,在这个中按部就班地开始



Launch



进入

cinp, sw



itch-ase

会进入

lsc.

CDS

,但

( 『 Org!MSP





是被

Marshl

: 分支







L exampl02

( c )调用

esc

esc

policy /v

sc

cpak,

口 t

ChaincodeStub

和 9 )在

put

S 阳 b 的函



Ch

c

pack

Chai

空, 。

则赋予

在此罗列一下

值为 的



teschain

3 个

Pack

参数,值为

exampl02

ge

生成一个

的名

对象

cpak

C ha



incodeDat

然后



cd

数据对



:=



( d)

ccpack) , ls cc. check ( e )调用工

sc.reat ),传入

。 )中进行简单地检查之后,就调用

Chaincode

stub.PSae(cdNm,y

),以 value

字、

cc. putChaincode Data (stub, cd

,执行部署任务

数据为

; 。



丑 codeDat(sub,

ChaincodeDt

若为

由于命令行未指定,为空值

iationPolicy ( chainname, ls

ai ncodeDat

: 分支

... ),查看

CDS

),进而直接调用

DEPLOY

/ vsc

args

InstantiationPolicy ( ... )分别获取并检查一个部署策略 Chaincode(stub,

得到的值

err : = ccprovider .GetChaincode

(),再由

l sc.getina





lsc.getCina(

的源码读进一个

. GetChaincodD

case

; chainme

。 ( b )调用 。



: ( a )先检查

(账户控制列表)

stub. GetArgs ()

xample02

数据;

’)”;

经存在于链上

FromFS ( . .. )将



ChaincodeStub

过的

:=

string (args [O

:=

DEPLOY



’ , ’ Org2MSP.meb

AC

调用

args

( ... )中,需要做如下事情

本是否可通过

cpak

stub

此刻仍

excutDploy

exampl02

中),

节)相似的过程,一

exe cu teDeploy ( ... ),开始部署巳

. mebr

8 )在

case

6.1

补,如

最后调用



节、

function

( ... )的参数:

depSc

6.15

每个值进行了检查,对参数修

excutDploy ” OR

巳章节(

( core/lscc/lscc . go

之后的

ar gs 值

Launch/Ext

1 步所提到的 而

,依次对



Invoke

是第 ,因



过的

会执



到调用



()将

... )中,依旧生成

7 )在此省略与



( c ) e.ndorsPpal





一同传人文件中的





! = nil

txsim

chainode.ExutC(

Ex cute

中 instal

( ... )中,会进入 ctx

Contex

131



中获取的交易模拟工具加入了 6 )在

ctx

l ( ... )中,不同于

Name == "ls cc " l

·:



mulateProps

ca

的设计与实现

txsim

工具先后放入了

Chaincode ( ... )之后,会进入 5 )在

chainode

的交易模拟工具和历史查询并赋值给

,同时将两



6 章

,把这一对

key

- value

exampl02



放到账本中

名字为

key,

Marshal



10) stub.PutState(cd.Name,cdbytes) ( core/chaincode/shim/ LSC

chainode 的

. go ShimHandler

中定义),调用了 的

stub.handlerPS(

. .. )时触

handlePutS

仅供书商

()函数



用途或交流学习使

stub



handler

是在第

7 步省略的过

发了

仅供

•!•

132

Hyperledger Fabr

程中,在 LSC

巳/ shim/





key

respChan LSC

意这里等待的

eTxCo

ntex

S 巳 rve

发状态机的

tx id



作为 的

trigeNxSaMs 给

发送应答

trigeN

。 STAE



三类消

发 一

执行

。 个交

执行的



函数,



3 步和



息,然后随

着函数

ChaincodeMsg_INVOKE

CHAINCODE 是

做什么的:新建

lsc:

.0 . 0 。

( d )进人

if 支





写集是

一个

rwsetutil/rwset_builder.go 键,映射一个

nsRW (写集)中



中的

,而 对

署也是一个交易,自然需要最终提到账本中只目前还没的时候

的部

终就 署

,这

( . .. )是由最初 . . . )创



的,创建 txCone

出来的是

Serv



Handler

关于

LSC

lue

,连同处

理 是





exa core/

mple02



ledgr

是将

映射,这个

/kv

key

map



value

lsc:

存储在这个

1.0



ledgr



/ txmg lsc:

/ 1.0



nsRW



目前并没有真正提交到账本(数据库)中,部 。

仅供非商业用途或交流学习使

( c)

pb. ChaincodeMessage_

==

rwMap

.. . )最 exampl02

重复

... )会利用 的线路

中的





va

map

Seta( 。



RWSetBuildr



txCone

. txsimulaor.Se(

,这个



这其中交易模拟工具在此之后将用到

txCone

map

个状

个交易

工具取出来后赋值给了



key 。

,函

获取

Excute

msg. Type. String ()

交易模拟工具,最终将

同写入到 写集

程中,

中的两个 字

接着 一

txid

的过

( )取

PUT_STATE. String ()分 中的

省略

handler.gtCRoNm

:=



)是为了防止同

ctx 两个

defr

_PUT_

口 tex(

historyQueExc

chaincodeID

的结束触发

ChaincodeMsg

7 步

如 即



更新到

( b )

,则顺利到达函数

handler.ctTxCo

5 步



一旦检查有错误,

可以判断它各

是在第

a)

巳 Msg

若中途没有错误

答消 矛日

Contex



writeMap

发送

巳 xtSa

的处理主体,分别

中的

txsimulaor



Handler

丑 try(msg.Txid

transcio

的同时,也将



(改一个状态),就是熟悉的增删

sendExcutMag

txCone

er

个正常的应

handler. creatTXIDE

下文



的布局:(

trigeN

( b ) handler. isValidTxSim (msg. Txid ... )会根据 易上

的信息



defr

支是函数

c haincode





一下函数

,发送 的消息

字基本就 a)

的回信

类型的消息,将只

烦,所以先讲

息,对应执行不同动作从名

体执行:(

Handler



_ DEL_STA

态、删一个状和调用

Serv

_PUT_ STAE

返回,触

ChaincodeMsg

等待

类型的消息





if ,大分

类型

handler.sendReceive

该事件函数整个都是异步执行的

贼值

中部的

时传人

ServHandl

看上去很长麻

extSaMsg

( c )

- case

ChaincodeMsg





( b)

_PUT_ STAE

数一样,定义了

赋值

巳 Stub

中存储通知频道,防止交易重复且随用删

的地方 函



ChaincodeMsg

map

。 一

handleit

cod

_ PUT_ STAE

txid

selct



最后发送消息

ShimHandler

的最后,

key

件函数

函数

defr

Chain

proto. Marshal ( &pb. PutState



然后进入

收到

13 ) entrBusySa

生成

生成的,这个函数类似于



usyStae



C haincodeMsg

依旧

creatChnl

S 巳 rveHandl

entrB 使用



( a)

一个

Txid

rHandle

是以



作为 将

,是

,都

12) LSC

装,

Paylod,

respChan

creat



ShimHandler



ion :

和 I value

),调用

异步发送给

handleTrsct

要做如下事情

消息的

(msg,

用途或交流学习使



()中需

ChaincodeMsg





例)

handlePutS

Info { ... } ),首先将 的

handler.go

ShimHandler

11 )在



i c 源代码台析与深入解读

core/haind 的

' Ii 二商



此,

仅供非商业用途或交流学习使



exampl02



后触发

chainode

defr

完成了

,调用



正的部署



ServHandl

发送携带部署结果



消息不做任何反应)

( e )将交



res

ChaincodeM

6 章



c hai

的结

机的



after

果放入

收到



respC

han

件函数



133

N extS tateMs g ,然

类型的消息(

ChaincodeMsg

ShimHandler

ServHandl

_RESPON

该事件函数调用

ha

发送消息,第

ll

对这个

步(

b )的

返回

15 ) 一

路返回



core

/ lsc

/ lsc.go

sendRci

ve ( . . . ) 等 handlePutS

中的

put

LSC



COMPLETD



完毕 型

17 ) LSC







触发

defr

,向

)函

ServHandl

收到



完成



Execut(

Serv

Handler

发送

CDS , Contex



ctx



同传人

这次进入没有通过

是第

calChinode



Launch

下执行

LSC



将 一直 执行下



成的是





传入的是

exa



CIS

C



ChaincodeSuprt

Launch 时

(.

模式,这里默认是



t e

解压



容器

,最后将巳

类型的消息

二次

进入



Contex



exampl02

,即

user

- Execut

没有

CDS



Launch



exampl02

canNme

的值为

)获取的

仅供非商业用

chainode

chrte

false

CIS



i 主或交流学习使用

mode

( b ) exampl02

为空



,因此生

CDS

,因此生 。

是在初始 的



( b )第一次

过,因此这次

Execut

Runs C

的 白,

LSC

!中

=

ExecutChaino

不同类型的消息都会被传人 core.yam

cccid Launch

a) userRnC

写入的,引用是

=

: ( a )第一次传人

第二次传人的是



err xample02

。 ( c )第一次传人的是

消息,

eds,

CDS,

不同于第一次的是

exampl02





Contex

)中,这里提几个字段的值:( net

chainodeHsBLu(

) 。

了,但

mple02

路返回,直至

4 步 的

( .

过,中途就返回

_ INT )



.分支,对应第 一

_ TRANSCIO

ChaincodeMsg

19

,这

, 建立

ChaincodeMsg 是

CIS



继续

中的

Launch



步所返回的结果



”“.

Execut

_

chainode.Exu

(.),第

中的

Contex



成的



中,

chainode.Exut

exctransio.g

Launch

15

core/haindx.g

LSC

ChaincodeMsg



中的

lsc

handler.

core/haind

5 步),继续向

cid.Name 二’'

函数,

消息和通知之后,

ccprovider . NewCCConte xt( . .. )并重新生成

的是



:,有到

Transctio

... ) 等待结束,函数返

完毕(对应第

if

是直接调用了



也执行完毕,并 handle

putils. GetChaincodeDeploymentSpec ( . . . ) exampl02



DEPLOY

数的本次部署

Paylod

. go

Chaincode ( ... )执行

过程



9 步

case

中的

消息,的

core/nds

)进入

stub

LSC

ChaincodeMsg

中的

18

重新定位

( .. . ),对应第

/ chain code/shim/hand! er. go

chainode_suprt.g 返回到

1 1 步

sendRecei ve ( .. . )

) 函数中的

Invoke(

core )执行





) 。

)继续返回,定位到

cc.Invoke( stub

口 codeDat

Invoke(stub

shim.Suce(dbyt

16

待结束

,

Chai

lscc. executeDeploy ( .. . )结束,整个 retun

)向



续返回,一直到同文件中的 返回

类型的消息,触发状态

ndler.sCha(mg

中的

在结束返回后,将开始 一路

过,因而

trige

向 _ RESPON

core/haindsml.g

dev

•!•

e 的设计与实现

handler.tigNxS

esag

ShimHandler

Respon

提到的



od



14) LSC





配 也



( c ) exampl02

化 项是否 没有



La

CDS

unch



仅供非商业用途或交流学习使

134



H yp

Execnv

e rl e d ge

r Fabric j 原代码分析与深入解读

值为 默认

中只



会进入

if

ChaincodeDplymtS

_DOCKER





据,进而获取

CDS

exampl02



Docker



容器





CDS



CodePackg

建立使用





launchAdWitForReg

ister

20

)在

用到,

exampl02



exampl02

Launch

的 }创

Docker

容器

/ dockerntl.g

21 )在 Docker

Star

镜像



中的

ID

,这



ID

Star(

),根 是

%s-

go-dckerlint





” %s





c ,

( b)

id

core



中保存的

Docker







IO

根据

的标准输出和

examp



镜像

建扫清障碍

ID 。

ID

( f ) err

atchSdou

分支,

事实

上(

f)

h )执行



Docker

使用标准的



方库

先创

全漏洞、运行笨拙等缺点),



exampl02

一个



的镜像和容器,为之

认可且以使用的

Docker

)创建容器

容器

个配置函数,即



( g )进入

的机制 是先积攒关于

dock 的

Launch





exampl02

if

err

巳 r.EN

oSuchlmage

镜像,然后重新执行

e x ample02

。 ( i ) client.

的容器,通过配置对象创建



镜像和启动容器

的过

巳 rclient build+Dockerf



exampl02

函数,预

nil ),启动 的





接口的改变而

github.com/fsza-dk docker



容器的标准输出和

e η

()

容器的方式可能会在以后根据

步创建

ID

( e) vm. stopinternal ( ctxt, . .. ) ,

镜像并不存在,因此将产生

prelaunchF

21

容器

将执行失败,因为创建容器的基础是使用镜像存在而当

builder

容器(



gorutine

CreateContainer(copts

StartContainer(containerID,

是第

生成一 门,获取

ID

的错误,在这个分支中使用了

)详解第

。 ( c)

chStdou

,尝试删除可能已经存在的同

client.

exampl02

2

容器的基础

= vm. createContainer ( ctxt, ... ),创建容器这个

然后调用

第一次部署的时候,

( f ) 创建

之内,否则

一 饨,

! = nil

ker

,这个配置对象除了基本容器的信息外还指定

getDockrHsCn

的 口 c (),创

ID

client

copts

exampl02

分支(该启动了两个

函数主要做的就是首先根据现有指向生成一个

配置对象

/

imageID,

:= vm.getClinF

err



、容器

a)

一个

.at

错误输出)不会进入

le02

后 的创

:(

,组装

Docker if

/ contaier

, 、.

fa ls e ,用于为调试目的而使能 器



字符范围在只有母数、

ol("vm.dcker

错误输出,这里使用默认值即后边的

最后

在部署中会使

追溯,将定位到

Doc

配置项,这个默认值是





client,

r.GetB



{



builder

客户端对象,这个是实际进行

( d ) attachStdout : = vipe



Launch

: = strings. Replace (image ID . .. ) ,根据镜像

containerID

别接收容



Launch,

一直

.. .)函数



builder 进

exampl02

的规则

用一替换,形式为

建一个



( . . . )函数中,启动了

: = vm.GetVMName (cid

err

DockerVM

源码数据,将供后文

建了一个



的源

(io .Reader, error)

exampl

sec

unch

if

exampl02

开始对

过程中,不同于

启动的是

dockerntl

.. . ),

La

exampl02

func( )

(

l 整个

命令放人的



builder=

点,可知

. .分支,接着进入

1 1

instal

return platforms.GenerateDockerBuild(cds) 调用



( ! chaincodeSupport . userRunsCC

! ( chaincodeSupport. userRunsCCll .,从文件目录中读取 码包

据此

o ( b )创





AC





首先概述一下

Docker

: ( a )使用的



器并不是简

单地

(因为这样产生的镜像有体积过大、存在额外安 exam

ple02



镜像数据(

Dockerfil

e 文件、

per

仅供非商业用途或交流学习使



节点的

tis 看

证书

出,对



容器进行减负,主要是去为编译

AC 空

. yam

!中

间和资源



chainode

项目提供,在 系

器,然



后把

中下

c巳

nv

exampl02



于生成支持的语言

/ chainode





来 中的

tis

package.tar 也





per





gorutine

中,

Dockerfil

core cenv

usr 中

,即向



gw

tw





写数据

件写人

tw



然后调用

/ chainode

/ platforms

Docker

Build

下载,

然后



chainode



一步

/ 中下载到输出流

后直接将

返回

read

uild



是接

。 收

返回到

21

步(

镜像数

- cenv

builder



ample02



令会



b i n package. tar (即名为

pl 巳 02

c h aincode

/ outp;

指定了

tw

,随后流

执行

容器





完毕将

然后通过调用

镜像部署

。 镜像的

Doc

exampl02

input



i nput

co

返回给

的可执行程序压缩包)

l e 指定的

从/

read, ,把

read Dock

2 1 步的(

k erfi

de 执行前

运行容器时

继续第

尝试

( d )异步

v m.deployag







Dockerfil



C md

是否存在,若不

fabric

g ) 处 据

exam

AD

fabric­

chain

exampl

这里需说明的是,启动容器

),进而

c haincode

写入



先把证书和

; OutpSream

并删除

ex



个值:

容器,等待编译完成最后将好的 第

围下的数据),将



行程序井放入/

镜像的输入流(即上下文,可以理解为此使用

能使用的哪个范



指定了

< ­

( c )在新

,依据

Options

WriteBysToPackg

OutpSream

input

对接上文,

- cenv



DockerBuild

fa b ric-env

fabric

< -gw

input

CDS.odePackg

,所做的就是根据选项先检查

建、启动

/ outp

erB

的 c util.

< - > Outp

镜像数据

中的 的可执

exampl02

容器的输出流,该最后也通过调用

ut

l e02

工 ls.go

chainode

该流为

环境变量

tw

Dock

指定了输入流,



GenratDockBuild(s,

镜像创建了一个容器,该的选项

InputSream

c ore.yam!

io. Pipe ()生成了一个管

:=

examp

/ ut

编译成名为



巳 Dockerfil

LABE



/ util



的可执行程序压缩

一些 inp



l ang

,然后

镜像,由

l e02

最终会形成压缩包并流

golan

exampl02

作为

examp

压缩对象,形成了



go

执行的是

Genrat

se os

目录下,还定义了

tw

i lder



平台的

output



inputFles

fa bric-a

( b ) input,



( . . . ),在这个函数

放入 golan

. . . )汇总了

定了编译命令,将·



per.ct

l/ bin



,然后再 car

ild

将编译生成的

/ loca



s 下是平台相关的代码,用

ckerBu

AD

genratDockBuild( 文

调用





建这

build·

a) bu

节点的证书

ic

l e02 创

然后详述过程:(

最终使用



解压到/

管道

examp

是总控文件,



exampl02

func ( ) { ... }中的

数据流

个能编译

大致过程为先

tform

文件(使用

令指定

inputFles go

语言

个容器,这 Fabr



/ pl a

Dockerfil

制并

将它放入了

连同

tw



/ chainode

此可以

为启动镜像,该由

的缩写

G enratDo

连接

项指

等),

core

中的

F ROM

bin

e nt

g olang

chainode.glrutm



( d )在

来生成可用的

建了文件头,



platforms.g

容器通过 le







/ platforms.g

genratDockfi

源码用到 一

的镜像所需数据包,

/ platforms

ample02 作

巳 nvirom

135

而存在的部分,这通常使

fa bric-env

里只关注

exampl02

ex

容器(由

go

代码,这

中,先把

•!•

e 的设计与实现

源码上传到容器中,然后启动时执行

AC 是平台相关的

( c ) 编译

c haincode



core

od

载镜像时会下,其实应该就是

,就是

编译好的可执行程序下载 jav

。 项指定的

Stared

统容器一

相对多)

. builder

Geting L i nux



AC

用较少,但占的 core

c hai

、编译后的可执行程序),然创建一个相对轻量级

AC

器将

6 章

创 erfil

fabric

g )向后执行

。 - baseo







仅供

136 令



H ype



并解压到/



dg

er

Fabric j 原代码分析与深入解读

usr/loca

Cmd

/ bin

的值(相当于

目录下,再者

creatCon

Dockerfi

suport.g



l e 中的

getArsndEv 当

时(准确说

exampl02



clie



程序



容器



Docker



args

co

fa bric-aseo

),

p er

执行

ntaierID,

nil)

只是其基 。

) 的是

fig

[)string {” chaincode ”,

且 t.SarConie(

-per.ads=0:751 容器中

Con

=

镜像,

client. StartContai ner( containerID, nil 以宿存在

建该容器的时候,配置

core/haind

exmapl02

c haincode



)是最初在

( ... ) 生成

fabric-seo 行

ai ner

CMD

fmt.Sprintf(" ... },所以 启动

' I 二商业用途或交流学习使



这里要进行



镜像)会执

晰地区分,执行

节点(这个可以宿存在主机中,也

chainode

-p

er

. a dr

巳 s=0.

0.:751





e xa

mpl

e 02

。 23

) 创

的程序

建巳

xa

chain

mple02

cod

的两个

e ,参看源码

exampl02.go core

func

/ chainode

旨在启动

/ sh 一

REGIST



即为

im



用了

(),

CORE

,把一

就可以获取第

2

shim.



设置

gr

exampl02



v iper

步最后启动



core

生成的,一路被传至

stream,

err

个流是连接



Handler



Ch

exa 了

mple02

取其他的环境



2

per

c haincode

节点的地址,在此则通过

flag

中执行了





而调用

.g o 中的

HandleChicoStrm

gprc

并调用

for 的部署 stream

ServerHandler

循环



一样

务端



c

发送了



a ndler 一条

flag stream per 数

Cha

相 in codeMsag

应成员,然后利用

exampl02

stream, 的

的 S himHandler

c

),如同

sec

ShimHandler

对象,将 向在

类型消息,

/

将被调用,进

了属于

ShimeHandlr REGIST

core

接收

exampl02

给定

Getter

节点在

( ... )函



.

步最后启

日,即通过

新创

,先创建了属于

ShimH

flag

()启动了循环

chatWiPer

赋值

的地址是通过

执行过后,由于

Registr

( b)

流,这

2

一步

chatWithPeer(chaincodename,

,通过

er

(.),因此

handler.pocsStm

。 ( c)

pe

grpc

取的,对应第

。 HandleChicoStrm

Server Handler 消息的





取一个



... )

creatConi

其中



. Registr

chaincode/chaincode _suport

且 dEnv(

.0 .0:7

获取这个地址



getArs

步中的

- per.ads=0

chainodeSuprtCl

_ CHAIN CODE _ID_

) ,获

客户端流



日志输出级别

suport.g

容器配置中,对照第

程序是

CORE

前缀

.idname

变量以设置

/ chainode

aincodeSuprt

容器时执行的

: ( a) 把



StringVar ( &peerAddress, "peer . address" ... ) 获 动

具体的过程如下 值的一些方法,如

E nv

_

SimpleChancod

code

streamGetter (chainodem

点的

ge



ing("cha 获

/ chainode

S tartlnProc ,

传人

C 。

容器时设置的



:=

per

Serv



mple02

exampl02

时调用的 C haincodeMs

viper.GtS

exa

均是最初在

exampl02

主动发送一个

L ifeCyclSs

的值,其次是



pc

在本容器内获取环境变

. ,这样

NAME=exampl02:nthrvsio

这些环境变

容器中执行

Start(new(SimpleChaincode ),该函

并通过



替换为

e 02

sec

LSC

SetupChaincodLg

mpl

定义,相当于部署

ShimHandler



exa

直接调

链码对象,相当于

设置为

在新运行的



per

exampl02



/ chainode.g

型消息给



m ples/ chaincode/ go/ ch aincode _ ex amp le02 /chaincode _

main

exampl02



er

exa

,执行的

数在

Handl

最后

启动了循环接

per

节点中的

仅供非商业用途或交流学习使





Serv 的

r Handler

消息的进程



ChaincodeMsg



( d )

_REGIST



属于

ex

Handler

ample02



替换掉



第二次进行

的 步,

R

S erv

直到

core/ha

in



code

/ex

)继续

执行

运行的

L a unch



c

( 即

pr

H an

ChaincodeStub

dler

。 的

l er





elaunchF

()预 巳 r 均达到

ready

。 Execut

Launch

(.











此,

返回定位到对应第

18

.)中



theCain

co

d e



发来 ha

exampl02

137

ShimHand

. ),

定位

到 的

rve

•!

b )新

ShimHandl 的

函数,进而调用

一个

计与实现

这里的注册指是用(

部分执行完毕,开始返回



Simple

ChaincodeMsg ( msg



) ),在这个函数

中:(

建并根据收到的

co de

容器中

消息,状态 a)

对象)的

Ini

O~l f



stub

C haincodeMsg

_ INT

( b ) handler . cc. I 口 it(sub

C hain

exampl02

handleit(msg)

_ INT

ndleit

(ChaincodeStub) , stub. init( ... )创

初始化了

c )中

core/haindsml.g Se



收到(

(.

接收到

口 ew

h )中

ectransio.g

端 事

步( 和

行结束

ShimHandler beforlnit

21

theCaincodSupr.Ex

( ShimHandler

的设

e r Handler

ServHandl



Support.Launch (.)执 24

把第

ute

c h aincode



r Handler

-Exec

Serv

消息,开始了注册的过程

e xample02

Launch

( b )中的

6 章

),调用了

:=

消息

S himHandl

t 接口,可以定位到

e r

exampls/

chaincode / go / chaincod e _ exa mp 1e0 2 / cha inc ode _ e xa mp 1e0 2 . go 的工

nit(sub

) 。

获取了

stub

b, 20

中的

( c )在

args

,分别

I n it( stub

中包

看作



a 账

[]byte (strco

似于第

的,所提交

14

key







A

交易模拟工具在

,只不 的账

户名



value

core/



hain

返回





进入



的读

A

和 的

中的



(.)也

写集(

的链码数据

这里

,此 的读集里面没有数据),然后返回至



Serv

Handler

收到 后通

... ) 结束等待 。

core

继续返回至



此,

第二次进行的

/ endo rse

si

步中

ex

ample02

是由第 的

13 写集人的

步中

/ endors.

mulateProps

LSC

()中

的写集入 N

Procespal(

/

it(sub)

Execut(

返回至

提交到

c ore

(),



巳通

然后返回到

txsim.GeTSulaonR

主要是写集中的数据

ChaincodeDt 作

分支执行





随之返回 。

key-valu

handler.cI

消息

Execut



exampl02 中



_ COMPLETD

18

都是

写集) )中,随

age

ShimHandler e nterBusySa

步中的

handleit(msg

( ... ),对应第 nil

100 ,

,之后



13

部分执行完毕,开始返回

!=

a,

[ ]byte ( strconv.

余额,最终也是将这一 的

同第

巳 Mes

on.g

txsim



r . handlePutS

中的

calChinode if

le

code/hain_suprt.g

Launch-Execute , Execut go



写集(

C haincod

exctransi

, 参

stub.PSae(A,

hand

/ handler.go





据,当前操



incode

发送

然后使用

ServHandl

chainode/smlr.g

defr



int

tub. PutState (B,

过这时使用的

的 ,触发

s

ShimHandler

core/ha

结束

2 0

c 指定的函数



exampl02:nothrvsi 的

户余额

)和

将触发

9 ~

b 账

的初始状态提交

25 ) stub. PutSae 类

,

Itoa (Aval 户

GetFunct i onAndParameters () 即,

10

)将两个账

的过程

stub.

函数和所用参,

户余额

且 v.

Itoa (Bval



)中,首先



B







取了交

e xamp

l e02

两个账户的状态数

.. .)中

,这里罗列





•!•

138

H y pe



dg

e r Fabri

c 源代码分析与深入解读

e . simulateProposal( ... )返回的数据: 返回结果

Respon

c d 为

,成功的结果中包含

exampl02







易的读写集(即目前所进行交结果)

ChaincodeMsg

_COMPLETD

为空(

core 空,

/ chainode

/ shim/andler

将继续执行 为

消息时所

e.ndorsPpal(

tesc

hain

res/

; txid

,交易

ID

只包含 26

)在



sec

的名字

LSC

似于

和版



的 为

Headr



的成功返回结果;[

值为

1.0



)在

calChinode



6.1

,这里



ctx

不为

; chainID

,值

Propsal

; cd/







个供

ESC

hdrExt.

使用的

CIS,

a )确定要使用的进行背书 数据,生成一个

巳 cis

Args

的值

; [ 3 ]口 byte

byte

:[





0 ]函

格式的

格式的包

;[

,这个作用

c

数名 id

exampl02



,这个



7 ] paylod

,为 c



id



ChaincodeDt

的权限控制,为



后边的背书过程中所用到数据均来自于此



Launch





6 步,只不过这期间 的

In

voke

()方法对

exampl02

28

)在

此可以与第

程查看 使用的

步(

6.13 是



( c )调用

ESC

Invoke

,传入

e Propsal

接着返回至

中的

Procespal

(),

e. 至此,

Proces 中



的构成 Endorse

cod

巳动作

Signature



( c )成员

是在





,产生了什么后果包含的有:

e /

签 步的(

c ) 。

()全部执行完毕,返回的数据 PropsalRen

,且该

ChaincodeDt



Endorsemt

PropsalRen

,包含了节点的

, []byte Propsal

cor

/

中的值, 26



Paylod

以及晗希过的 即谁干了什么

stub

/ sec

自此一路返回,过程省略直接定

生成的

1 。 ( b )成员

ChaincodeAt ,

core

Args

osal

exampl02

用到

中的每个值都解压出来,在

巳 PropsalRen

,明确版本固定为 签名

格式的

rop

被赋值为

Version

和签名者的 byte

Creat

Respon.Payld

: ( a )成员

到 的

一直对

最后调

callChaincode ( . . . )执行完毕,对应第

prots/uilx.g

PropsalRen

直接定位

。 ( b )根据解压出来的 。





。 Args

名并整理应答数据,最后返回这个里不再详述 endors

后 ,

Invoke

: ( a )分别将携带的



5 步之 Handler

中生成的

b )处的对照,看都是哪些数据



的两个

handleTrsctio



Launch

节 。



中,只做了两件

26

过程,且

的交易结果进行背书 的

Invoke

Launch-Exet

中途的过 一直

chaincode/ shim/andler.go

chain

最终返



Propsal

体过程如下:(

Paylod

escc / endorser onevalidsgtur.



参数

是生成一

一次

已经被

节的第

ESC 的是

包含[]

定义的事



CIS,

中,这次只会执行

ESC









途返回,因为

位到



做的就



; [ 4 ]口 。

27





6 ]事件,为

前版本对这个字段并没有使用

call Chaincode

exampl02

chainID

S igned

和图中的

5 ] 交易读写集数据;[ 当





继续,由于

该函数

和准备的 的

时的



( b )根据参数

creatCIS

version





; [ 2 ] Propsal

LSC,



应图中的





中的

[ 1 ] Propsal 值

Name

来执行背书

本号

ExecutChainod

pl 巳 02

hdrExt.PayloVisb

l Chain code

ESC

exam



下传入



... )中 ca

Name

/ pro

endorsPpal(

然后通过再次调用



中) 一

; signedProp

个值为

LSC

le Transctio

均为上文返回的数据;

Chaincodel



hand

部署

ChaincodeStub

...),在此罗列

/ simulatonReJcv





LSC

; simulatonRe 为

携带的

. go



ChaincodeDt

; cevnt 回

res



格式的

签名者

PropsalRenyd,

ChaincodeAt Chainco

相当于在描述 del

一 里的数据





LSC 了



1.0

,

exampl02



集,

Events

样的



数据是

LSC

C haincodeDt

为 一

Respon



,



( 4 )一



Result



的 返回



含了

LSC



Respon,



Paylod

per

继续

Propsa!Ren

执行,

生成“ comn

. Envelop



据的过程

res

,其



Paylod









读写

ChaincodeDt

/ instae



. go

完毕

139

后两条链码

exampl02



instae

这 中





utils.CreaSgndTx

一封

〈-

exampl02



/ chainode

o d e 的设计与实现

返回的

署完

cf.EndorserClient.ProcessProposal( .. . )执行

” ) instae

c h a i旧

exampl02

则包

Suces

Propsa!Ren

部署

6 章



结合

Propsal

,返回的



Envelop

数据

的封



过程

是一



具体数

据到

被一层 包









通用





广播

6.12.4 在

per

/ chainode

/ instae

. go

instantiate(cmd,cf )返回的 进行广播

E nvelop



紧接

chainodeDply



着被



env,

err

:=

cf.BroadstClienS(v)

。 per

命令

Client

, 象其



的广







播客户端

为在

装了一



grpc

连接

ordeing



个连



服 务客

com

.go

接在





e nt . pb.go



/ comn



AtomicBradsC!

orde/ab 端

per



中的

定义)





命令执行

, 散播



中心

之 接

初就

收,

程 后,

可 的





:即







orde 网

e nt

客户

端对

象(这 prots

知,这是连接

orde

,若未指定





cs





是在



per

cc

/ 的





/ comn

发送请

求获

/

取的





。 ) 将巳

nv

交给了

发送



orde

服 务 将

最终提交到

tCli

broadcst

s t 流客户端,在

,通过

经被建立

中的

as

Broadc

名就

指定

( env

只 讲大概过

/ serv.go 。

数 据排 节

o rde

服务中进行处理

env

络中的每个



序后发送给

per

点的链(账本)上



由于涉及

orde

节 点 的

go s sip



服 务 开



部署后的状态

6.12.S 部署后的状

态分为

口巳



xample02 口



exampl02

的证书

D exampl02

几种:

数据被放入交易模

镜像被

,可



执 的



镜像



上下











写集中



Dockerfil



件,





tls

连接

p e er



。 创

e r 通 的





行程序压缩包 容器被

ServHandl exampl02



ChancodeDt 的







,即

Chain

Broadcast (.)处 播



GetOrdEnpoi~f

instae

务,这里

一部



命令行

cf.BroadstClienS

始散

_ Broadc 的

从所



e nt.go

AtomicBrads

客户端



的地址

/ ordecli

信 Serv

Chaincodes . chainodeMp





单独运行

ShimHandler

,并通



g 叩

c 与

per



。 Handl

er



p er 中

节点的 进

行了注

the 册

C hain 。

co

deSu

p port.

r unig





•!•

140

Hyperledger Fabric

D exampl02



源代码分析与深入解读

D exampl02

ShimHandler



指定的{

户余额

1 『 Args

10,

exampl02

B 部

Serv

Handler

”:[

账户余额

20

int

署的数据通过

均处于

'『,”

a ”,

orde







服务和

gosip



chainode

6.13

务要

做的

拟器的

序后散播到网络 部署

exampl02

『?]},即

写集



的各个有

效 本身做的

A



节点中并

最终提

事情,只是会促成



建资产(键值对)的基本范





选择一个代码存放位置

6.13.1 首先需要确保

Go 为

20

操作步骤

本节是一 个在账上创

chaino de







经被完整安装

到系统上

应用创建一个位于

使用如下指 mkdir

事情)



’' b ",”

/

易模 per

orde

状态

” 100 1l

,这两个状态被放入交

交到各自的链(账本)上这一点其实不是



ready

$GO



PATH

。 /src

/目录下的子





『 p $GOPATH/src/sacc && cd $GOPATH / src /sacc Now, let ’ s create the source file that we 『 1 fill in with code:

使用以下指令创建源文件



touch sacc.go

内务处理

6.13.2 首先 别是

,进行内 In



it 和

Invoke



shim packge



package 工 mp

处理 数接口

per

mai



对于

每一 。

protobuf packge



所以首先为

chainode

,都会实现预定义的

chain

chainode

引人必



的依赖





code 此引入





( ort

” fmt ” ” github

.com/hyperldgfabi

工 ncode/shim

"

"github.com/hyperledger/fabric/protos/peer ”

初始化

6.13.3 实现

Init



chainode 数



I I Init is called

dur 工口g

chaincode instantiation to initialize any data.

chainode

接口,特



〈- 的设计与实现

ch ain code

6 章 第

fu n c (t *Simp l eAsset) Init(stub shim.ChaincodeStubinterface) peer . Response

In 函数来获取

. GetSringAs erfac

巳 Stubln

C haincod 。

检查



I n it ”方法 初始化的东西,则提供一个空“

调用

chai 升级现有

cod

特别地,如果没有“迁移”操作或其他需要在升级中 。

函数 Init

f 参正 需妥确保适当

c h ain 当编写的

。 函数

ifoj JfJ i i'-

级同样 1t-

chainode 。主

it 所需的参数,并进行有效性

。 本例的传入参数是一组键值对

II II II II

any 工 ze 工 al Init is called during chaincode instantiation to int 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 { proposal 工 on II Get the args from the transc args := stub GetStringArgs() i f l en(args) != 2 { return sh 工 m Error (” Incorrect arguments Expecting a key and a value")

。 将初始状态存入账本

r.Respon

pe 常,会收到表明初始化成功的

inc Cha

调用

返回对



如果一切正 。

并以键值为参数传入 erfac

o deStubln



II Init is called during chaincode instantiation to initialize any II data. Note that chaincode upgrade also calls this function to reset II or to migrate data, so be careful to avoid a scenario where you 11 inadvertently clobber your ledger' s data! func (t *SimpleAsset) Init(stub shim .Cha incodeStubinterface) peer.Response { II Get the args from the transaction proposal args := stub.GetStringArgs() if len (args) ! = 2 { Expecting a key and a value " ) . 口 ts return shim .Error ( ” I ncorrect argume

II Set up any variables or assets here by calling stub.PutState() II We store the key and the value on the ledger err: = stub.PutState(args[O], [Jbyte(args[l])) i f err ! = ni 1 { to create ed (" Fail 工 n tf . Spr return sh im .Ero(fmt return

s hi

m

.Suces(n

工 1)

aset



辛S

”,

args

[ OJ))

141

•!•

142

H y perledger Fabri

6.134

调用 添加

i卖

chainode

Invoke

/工

c 源代码分析与深入解

函数签名: is called per transaction on the chaincode. Each transaction i s

nvoke

II either a ’ get' or a 'set ’ on the asset created by Init function. The ’ set ’ II method may create a new asset by spec 工 fy 工 ng a new key-value pair. *S

func (t



工 mpleAst

似上述

In

)工

it

函数,

nvoke(stub

sh

需要调用

工 m.Chaincode

来获取参数

chainode set

先调用



get

peer.Response {

C haincodeStubrf

数所需的传入参正是应用想要调 能函数一-

Stubinerfac)

的名称



工 nvoke



在本例中,只有两个简单的功

:前者允许对资产的数值进行设定,后获取当状态



ChaincodeStublrf

. GetFuncioAdParms

数名与参



来获取

chain

cod

巳应用所需的函

。 II Invoke 工s called per transaction on the chaincode. Each transaction is II e 工 ther a 'get ’ or a 'set ’ on the asset created by Init function. The Set II method may create a new asset by specifying a new key-value pair. func (t *SimpleAsset) Invoke(stub shim . ChaincodeStubinterface) peer.Response ( II Extract the function and args from the transaction proposal fn, args := stub.GetFunctionAndParameters()

使

set



Suces 列化为



get

这两个函数名正式生效,并调用些

chain

gRPC

shim.Ero protbuf

函数返回一个合理的响应 消息

这两个

shim

应用函数,经由

shim.

成员函数可以将响应序



II Invoke is called per transaction /巳



code

o 口 the chaincode Each transaction is a 'get' or a 'set' on the asset created by Init function. The Set II method may create a new asset by specifying a new key-value pa 工 r. func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubinterface) peer.Response ( II Extract the function and args from the transaction proposal fn, args := stub.GeFnc 工 onAdParmets ()

ithe

r

var result stri 口 g var err error i f fn ==” set" { result, err= set(stub, args ) } else { result, err= get (st ub, args) if err != n 工 1 { return shim.Error(err.Error())

II Return the result as success payload return shim. Success ( []byte (result) )



实现

6.13 .5 此 注意,

chain

chainode



cl 山

ncod

e 的设

计与实现

•!

143

应用

code

应用

就像上文

6 章





现了两个函数,



样,通过调用

可以被

chainode

ChaincodeStublrf.G

Invoke



shim API

数调

函数来访问账本

中的

用,下面将实现这些函数



ChaincodeStublrf.P



II Set stores the asset (both key and value) o 口 II it wi ll override the value with t he new one the

ledgr.

the key exists,

工 f

fun c set(stub shim. ChaincodeStubint er fac e, args []string ) (string, error) { i f len(args) !~ 2 { r eturn H ” , fmt Errorf("Incorrect arguments Expecting a key and a v alue ” )

err:= stub.PutState (args[O],

[]byte(args[l])) err != nil { return ””, f mt.Errorf( "Failed to set asset

工E

return args [l

],口

%s ” , args[O ] )

il

II Get re turn s the value of the speci fi ed asset key fun c get(stub sh 工 m .ChaincodeStubinterface , args []string) (str ing, error) if len(args) != 工{ return ””, fmt . Errorf (” Incorrect arguments Expecting a key ")

value, err:= stub.GetState(args[O]) if err != n 工 l { return ”” , fmt.Errorf("Failed to get aset



with

毛s

ero



辛 s ” ,

args[

err)

O ],

i f value == nil {

return

””,

fmt.Errorf

(” Asset

not found



毛 B ”,

arg

s

[O])

return string(value), nil

整合全部代码

6.13.6 编写

main

函数,它将调用

s him.Star

函数



下面是包含





c h aincode

package main 工 mp

ort

( "fmt"

” g it hub com/hyperledger/fabric/core/chaincode/shim ” "github.com/hyperledger/f abr ic/pro tos /peer"

II SimpleAsset implements a simple c haincode to manage an asset type SimpleAsset st ruct {

程序的代码



144

•!•

Hyperledger Fabric j 原代码分析与深入解读

II !nit is called during chaincode instantiation to initialize any II data . Note that chaincode upgrade also calls this function to reset II or to migrate data func (t *SimpleAsset) Init(stub shi m.Chainc odeStubI 口 terfac) per . Respo 口 se { II Get the args from the transaction proposal args := stub . GetStringArgs() if len(args) != 2 { return sh 工 m .Error( "Incorrect argume 口 ts Expecting a key and a value ” )

II Set up any variables or assets here by cal

工口

g

stub PutState ()

II We store the key and the value on the ledger err:= stub.PutState(args[O), [)byte(args(l])) i f err != nil { retu 口 shim.Ero(ftSpn (” Failed to create retu



shim.Suce(nl

aset



每s

”,

args

[OJ ))

)

II Invoke is called per transaction on the chaincode. Each transaction is II either a ’ g et ’ or a 'set ’ on the asset created by !nit function. The Set II method may create a new asset by specifying a new key-value pair . func

(t

吃工

mpleAst)

Invoke(stub shim.ChaincodeStubinterface) peer.Response {

II Extract the function and args from the transaction proposal fn,

args := stub.GetFunctionAndParameters()

var result string var err error i f fn =咆 et " { result, err= set(stub, args) } else { II assume 'get' ev 口 if fn is nil result, err= get(stub, args) i f err != nil { retu



shim.Er

ro(e.E)

II Return the result as success payload return shim . Success ( [) byte (result) )

II Set stores the asset (both key and value) on the ledger. If the key exists, II it will override the value with the new o 口 e func set(stub shim.ChaincodeStubinterface, args i f len (args) ! = 2 { return "" , fmt Errorf (”工口

err := stub.PutState(args[O),

coret

[)string)

arguments

[)byte(args[l)))

(string, error)

Expecting a key and a va

{ lu

巴”





if err !=且 il { return "”, fmt.Err orf ( ” Fail ed to set

6 章

c h a incod

aset



毛 s

e 的设计与实现

”,

ar

•!

145

gs[O])

return args[l], nil

II Get returns the value of the specified asset key f unc get(stub shim.ChaincodeStubinterface, args []string) (string, error) if len (args) ! = 1 { return ””, fmt.Errorf (” Incorrect arguments. Expecting a key ” )

valu

巴,

er

=

stub.GetState(args[O])

i f err != nil {

return ”” , fmt.Errorf (’' Failed to get 工E

aset

:毛

s

value =口 il { return ””, fmt.Errorf (’' Asset not found :毛

return str

ero

with

s ” ,

:告

s ” ,

args

[0] , err)

args[O])

nil

工 ng(value

),

II main function starts up the chaincode in the container during instantiate func main () { if err := shim.Start (new(SimpleAsset)); err fmt . Printf ( ” Error starting SimpleAsset chainode

编译

:毛

s ” ,

er)

chainode

6.13.7 编译

{

!= n 工 l

chain

code

的代码如下:

go get u tags nopkcsll github .comlhyperledgerlfabriclcorelchaincodelshim go build --tags nopkcsll

成功

则可以进

行下一步:测试

通常



chain

可以由用户

code

创 chain code

orde

如果你之前还没有进行过这



建井启动



per

节点启动并维护



安装

chai

、 。

c h ane

不过,在“开发者模式”下

当用户处于以快速编码、构建

开发阶段时,该模式十分有用



6.13.9

d 巳 。

在开发者模式下测试

6.13.8



c h ainco

l 来启动“开发者模式”



Fabric

运行、调试的循环周期为主

这样,用户可以



即编译

chainode

并调用函数

样例 步,请先安装

e

借助构建自带区块链样例网络时已经预先生成好



Hyperldg

ncod

Hyp

巳 rledg

Fabric Samp

le 。

下面进入到安



•!•

146

装好的

H yperledger Fabric

源代码分析与深入解读

fabric-smple

下的

cd cha

下载

Docker 的

模式”成功运行



·~

你选



建脚本

,我们

需要

四个



,那

镜像来确保“开发者

克隆仓库,并按照指示下载了

么你的本地理应早已安装好了所需

Docker

择手动拉取镜像,那么务必将其重新

images

Docker

f abric-smple ar ies

如果

Docker

如果你已经安装了

platform-secibn

-



镜像

根据下载样例中自带





工 ncode-krvm

6.13.10

”‘

chainode-krvm

标记为

指令可以方便地查询本的

Docker

镜像 lates

镜像列表



。 。

输入

docker

你应该会看到类似下面的

内容: docker 工 mag es REPOSITORY hyperledger/fabric-tools hyperledger/fabric tools hyperledger/ fabric-orderer

hyperldg/fab

工 c-orde

re

r

hyperledger/fabric - peer hyperledger/fabric-peer hyperledger/fabric ccenv hyperledger/ fabric-ccenv



善女口果你通过

downla 像资源列表

” platform-seci

。 录

6.13.11

IMAGE ID

TAG latest x86 64-1.0.0 latest x86 64-1. O. o latest x86 64-1. o. o latest x86 64-1.0.0

缸-





立的

终端

CREATED hours ago hours ago hours ago hours ago hours ago hours ago hours ago hours ago

SIZE 1. 32 GB 工 .32 GB 179 MB 179 182 182 1.29 1.29

MB MB MB GB GB

bin

不过这里我们只关心以上四个

下打开三个

4 4 4 4 4 4 4 4

e09f38f 8928d e09f38f8928d Odf93ba35a25 Odf93ba35a25 533aec3f5a01 533aec3f5a01 4b70698a7ld3 4b70698a7ld3

现在请

c haincod

e-dockr

-d ev

mod

e





1 号终端

执行如下命令: docker-compose -f docker compose-simple yaml up

上述指令启动了



“开发者模式”下启动 是

个带有

。 CLI

命令行

,可

容器中,所以我

们下



6.13.12 执行如下命令:

2 号终端

Si ngl

mpleMSPord

profile

它还启动了另外两个容器:



c haincode 面马上

eSa

进行 跳转



交互 chainode



创 调用

建并加入

个包含

chainode chanel

部分



的网络,并将节点在

(管道)的指令

运行环境;另一个 内

嵌于

CLI



docker exec

-工 t

cha

6 章

chainode

的设计与实现

·:·

bash

工 ncode

执行完上述指令后,你应该会看到如下内容: root@d2629980e76b: / opt / gopath/ src / chaincode#

(此时已经进入

chainode

容器)下面编译你的

chain

code:

cd sacc go build

现在运行

chainode:

CORE_PEER_ADDRESS=peer:7051 CORE_CHAINCODE_ID_NAME=mycc:O ./sacc

chainode t9.\



注现阶段

per

节点启动,

chain



实现

chain

code

code

还没有与任何

日志表明

per

chanel

关联



节点成功注册



这会在接下来使用

instae

指令后



3 号终端

6.13.13 即便处于-

per-chainodv

这样生命周期系统

模式,安装 chain

chainodev

code

chainode

模式中省去

才能正常进行检查 。



下面进入

CLI

这一步仍必不可少,

容器进行

也许这一步会在日后的-

- per­ chainode

调用



docker exec -it cli bash peer chaincode install -p chaincodedev/chaincode/sacc -n mycc -v O peer chaincode instantiate -n mycc v O c '{” Args": [吐”,门。叮)’- c myc

现在执行一次将

a 的值设为

20

的调用:

peer chaincode invoke -n mycc -c ' {吐

最后查询

a 的值,会看到

20

6.13.14 重启网络来轻松地测试它们

虽然只实现了

测试新的

”.[咀

et

”,吐吃。

l}'

C myc



n mycc -c

peer chaincode query

rgs

’ { 咀

rgs

”:

["query ”,

sac

chainode ,不过可以通将同的



chain

重启它们将可在

chainode

code

容器中被访问

添加到

chainode



子目录下

147

............. ……·· ...... ..... . .. ... .... . .. • 11

7 章



’配~万

汇 fN'"t-

成员服务提供者 MSP

中的 Fabric

)等在

imp lementations, and the sign, verify, authenic

( cryptogahi 认证

签名、校 、

作 密操

加 解

) 理

Service Providers ( MSP Membrship

地 本章将让你更好

验、

虑的地址方 主要考

为 密

私和机 了隐



) 。

a rios

s sc 巳n

a dres 案(

实 助

,帮 对

配 相

存在与能力 的

c h anel 的概念,与

” 权

“授 的

Fabric

这个 。

和支配 作

)被操

channel lev 层级频道(

) 和 network

broade 络(

网 广域

在 制可以

控 权限

结果,数据 。

户或客端的应用 用

终端 ,

成 构

于组织、网络的 用

这些被试

( cryptographic certificates ), 书

密证 加

)被用来生成

Key Infrastuce Public

P 阻, 设施(

础 基

公匙 。

础 为基

以此网络 络,并



份 的交易 身

个明确 一

有参与者都 个所

提供一

F abric

。 应用

的设计思路 MSP

7.1

验证书和

) 、 isung

论( 争



认 成和



签 名的 规则,以及

份标 识、验证 的一套身

己 定义它自

可能

在 存

藏了

。 个组成部分

一 作为系统的

象, 的抽

架 作构

机制和协议,隐 密

有加 了所







。 方法

区 Fabric



MSP 一个

。 证

用户认

抽 MSP

别的, 特

系 会员关

个 一

供 于提

着眼 个

是一 MSP

。 实践细节

的 安装上最好

的 MSP



供关 于提

力 本章着

下文

我们将

详 细介



工 间协同

架 系标准和构

会员关 同的

MSP



操作

管 MSP

个 或多 个

被一 会

域链网络可能

员 会

块化的 模

供了 提

理,他们

。 作的能力

体系,并



论关于

MSP

方面















作和





7.1.1 MSP

MSP

,它的配置文件必须在每个

per

的本地中被指定,开启频道上

成员服务提供者

•!

149

per



orde



orde

(去使能

per

并排序签名)

町、客户端的身份认证,以及所有会员各自

。 首先,

MSP

需要指定一个名字在网络中代表自己,如

mspl,org2

员关系规则中代表一个

chanel 对象的

ID ID

MSP

配置

为了启动一个 签名认证

7 章



中的 一

( MSP Identifr

MSP

) 。

个共同体、组织或者分部

每个





MSP

实例存在于系统初创的

MSP





chane

ID

是唯

l ,



orde





这个名字在其会

也被引用作为

MSP

例如,若检测出来存在两个相同

将会启动失败



在默认操作的情况下,一个参数集合需要被指定(也就是

MSP

员),用于身份认证(

identy

些参数由

RFC5280

/ certifa

巳 validton

)和签名认证(

需要哪些成

signature verifcaton

) 。



指定,包括:

口一个自己签名出的证书(

sel

of trus

巳 signed

)列表,用于作为信任机制的根证书(

rot

,这个根证书应该是:把自己签出来的分发给他人拿着核

对时,在根证书列表中查询核如果存于这个则说明人是受

信任的)



0

该列表可以用

rot

CAs

表示



考虑到证书验,提供一个用于代表(作为)中间人

CAs

这些

CA 数)



口一个

证书应该是在

rot

该列表可以用

中被鉴定过的

intermda

X.509 向

of trus CAs

表示

rot

of trus

中的证书



中间人

CA

证书列表

表可以用

a dminstrao

CAs

一个组织单位列表,

MSP 的配置(即改变

表示

MSP

证书是可选的(参

OU

。 CAs

、中间人

CAs

有效的成员应该包含在这些组织单位

X .50

。 rot

该列表可以用 CAs

OU 或

TLS

口一个用于



TLS

对于一个

MSP

intermda



CAs (废除



X.509

根证书列表(

X.509

self

中间人证书列表

- signed

该列表可以用



这是一个可选参数

(X.509) certificates, TLS root of trus

) 。



对象实例来说,有效的身份需要满足以下条件:

格式的证书,并包含可验所在路径该对应到

rot

口不包括在任何一个

CRL X.509

表示



口一个用于

口在



并为组织

List



X.509

该列 。

巳 CAs

的证书也是从这里面来,而不凭空产生)一个可选参数



) 。

9 证书中

CAs, intermda

字段时,才会使用这个列表

一个废除证书列表,每都可以对应到

表示

这些证书的

。 rot

成员保留了一个

管理员的角色 rot

是一个可选配置参数,当多组织使用同

CRLs





所以该证书列表代着 MSP

Q

X.509

证书列表,该中的包含可验路径而这些对应指

拥有者被允许发起请求改变该 0



证书的

格式的证书结构体中



CAs



。 OU

字段里,存在一个或多属于

OU

List

的组织单位



•!•

150

Hyperledger Fabric j 原代码分析与深入解读

更多的

MSP

身份的有效性信息,

请阅读

除了验证相关参数,对于

M

用于节点签名的匙( M 注意的是,

SP

MSP

废除

TLS



MSP X.509

),

参数中(

当前只支持

rot

EC CAs

DSA

和中间人

keys

CAs 加

入到



节点的

X . 509

中)的有效证书

C RLs



证书,







需要重点

外,当前不支持强



证书和官们的签名匙 证书去填

不支持证书中包含

MSP

RSA

替代

keys

的配置,我们可以使用

c 叩

巳 dger

opensl



这里强调一下,

Fabric



,我们可以使用

Hyperl

Fabric CA

ptogen

tol





这个项目也可以用

Geting

Stared

来生成

M

SP



有详述



配置所需的

keys

和证书



MSP setup on the peer & orderer side

为了建立本地

MSP

目录,如

$ MY

CAs

2. caerts





orde

上的,

管理者都要



建一个

含如下子目录和文件:

pem

录,

3. (可选)

文件,每个

包含

每个



pem

pem

intermdacs



4. (可选)

config

5. (可选)

crls

.y aml 目

6. keystor



应一个

文件对应一个

rot

录,对应中

adminstro

证书

间人

文件,包

CA

证书,即



OU

相关的

CRLs



即 的

信息

rot

CAs





含关于组织单位

录 ,包

目录,包





含一个有节点签署密匙的

PEM



件;我们

着重于当前不被支持



7. signcert

目录,包含一个有

8. (可选)

X.509

tlscaer

9. (可选) 文件

,包

per





秘钥

),无论是

/ mspconfig 目录,包含

administrator

RSA

( local MSP

_PATH

1. admincerts

PEM





为了生成

7.1.3

Identity Validity Rules



息永不消失,只能被

如何生成

作为



中的身份信

的证书

7.1.2

一些

SP

来说,使节点能够去签名或授权你需要指定

signing key

该证书必须是在



SP

M



录,

包含与

整数的

TLS

tlsinermdac



PEM



CA

文件

证书



一对应的

录,包含一些与中

PEM 间

TLS

CA

文 ’ s 证书



。 节

点一对应的



7.1.4 Channe l MSP setup 在系统初创的时候,出现网络中所有 被指定过(

MSP 即必

一 CAs

下,

MSP 、 OU

须已经存在),并被包含到系统

的验证元素(即各种书、配置)都需要

验证元素由

c hanel

List, CRLs

组成

MSP 。

身份标识( 在

MSP orde

进行体系解析时,统的

identfr

的创

) 、 rot

世纪块(

gens

C 挝、 创

intermda

i s block

世纪块被用于

CAs

)中



、 admin o rde

回忆



据此

orde



被允许去认证

chanel

chanel

l 的



MSP

)的

如果创世纪块包含了两个相同身份标识的

l 的创世纪块中



l 之前,保证包含在该

chanel

配置信息的正确性是应用责任

configtxe

工具(

的验证元素(如

rot

configtxgen tol CAs

、中间人

CAs

c hanel MSP

中的

上的

一个 MSP

Admin

CAs

如此之后,被

,包括该

MSP



中的一员(即拥有

admin

chanel



,通过将

MSP

专用的配置目录,并在

configtx.

工具配置该

chanel



关的废止证书列表(

MSP



CRLs

Admin

config update object

更新

创建一个或多

的创世纪块(或者是最新配置)中

等)放置到

MSP

点)创建一个配置更新对象( 。

chanel

configtxe

重新配置一个

配置

我们强调,在

)帮助下引导启动

文件中设置相应的选项,我们可以使用

通过该

钮,



当在

ya ml

M

,其验证的各个组成部分也就是(只能)管理该

chane

chane

MSP

chanel

,必须存在于

加入该

151



aplicton

per



•!• 成员服务提供者

有认证的材料了,如果创

然地,整个网络的引导建立也会失败

对于应用( chane

MSP

orde

该请求)

将拒绝此块,自



的创建请求(等于说

的请求合法才通过

orde

7 章

CA

)的公告,

证书的节点,也叫作

ad

),可以实现一个

c hanel

管理的客户端应用将宣布这个针对该



MSP

min

MSP





所在的

重新

chanel





最佳实践

7.1. 5 本节我

们将 详述

MSP

1 . 组织单位和

配置在一般情况下的最好实践(

MSP

我们要求

MSP

best

practies

) 。

之间的映射 与组织(

organizts

类型,需要考虑如下内容

)之间是一对的映射



如果选择一个不同的映射



一个组织雇佣多种()

MSP



这对应了一个组织包含多不同部门,出于独立管理或

隐私(权限)的原因,每个部门有一

MSP

一个

MS 出其他

P 所拥

子部

的身份 门)分



这样做的意义在于,一个节点可能通过

组织中其他的



这对应了多个组织以类似的关系被管理成一协作整体 。

per

per



将传递组





这是一个

MSP

下的

定义的颗粒度(

- organiztl 访问权限,如图

per

,而且

granulity)

一个组织有不同的部门(称为单位 chanel

units

7 - 1 所示

) 。





。 第一

)中(即将所有成员的证书之

MSP

否是属于自己的同一个组织

节点配置的局限

这里我们需要知道的

织范围内的消息给同属与一个

中的任意一个部门可以给予多不同

两种方法如下

中识别

只与从属于该节点的部

,如多个学校组织联合起来成立一盟)的情况 per

P

。 MSP

会忽略这些

只能被

MS

gosip

是,一个组织中的多

ship

per

享组织范围内的数据,而不是与所有成员分

nsortium



在这种情况中,一个 一

多个组织使用一 (co



有(代表一个层级上的部门),也不能从同

per 门(

代表的情况

种方法:定义

一个

MSP

,将所有组织的成员纳入到该 类的数据纳入到该

MSP M

SP

) 。



MSP

的成员关系( 的配置将由

me rot

mber­ CAs



•!•

152

Hyperledger Fabric j 原代码分析与深入解读

中的所有成员)当作是同一个组织内 MSP

节点的本地 gosip

(即 per

中的 MSP

在其本地

会把那些 per

gosip 个限制就是

一 该方法的

。 )策略

endorsmt 的背书(

code

c hain 者一个

)策略,或

/ write read

的读写(

c hanel 一个

的所有成员,该策略可以用于组 OU

获取指定

)被定义为 Polices

策略( 。

OU 组成,会员身份将包括所有的

CA admin



intermediate CAs

。 成员,结果也自然会向这些节点传播组织范围内的数据(比如它们状态)

日|… Membership Services Provider

IllPeer

c Global MSPs

10 时 1

囚|叫 关系图 组织 MSP

7 -1 图

,这将涉及每一个部门的指定问题即 MSP

第二种方法:为每一个部门定义



中分离客户端 per

从同一个组织中的众多

MSP 一个

门定义 一个部

扩展为每 OU

的 MSP

我们也可以通过使用 。

,但这规避了上一种方法所出现的问题 MSP

里的缺点是要管理多个

orde 而不是客户端或扮演

。 的节点)

。 以下是对该需求的有限支持

创 -种实现这分离的方法是为每一类节点都

的 orde



chainode 节点可以限制某些系统 ”请求

admin (终点用户位于请求的起源处)

MSP 。

我们可以绕开这点的前后矛盾,如果接受对于

。 MSP

运行到本地的基于策略

签名的话,

per 。

MSP 依然属于同一个

per 不会被大幅影响,因为同一个组织的所有

客户端类型的

per 实例中,

MSP 到两个

最终,这个组织将会被映射 。

相互作用(交流)了 client

gosip

MSP 节点的

per 义)只使用代表

自然可以与

,而背书策略(定 MSP

该组织应(可以)访问的频道,需要同时包含这两个 。

/ per

一个 的,

一个客户端 一

一 -

CA intermda

的 建一个单独

MSP 的;配置两个不同

per/od 个客户端的,一

“ joinChael

这种 per

在很多情况下,从身份自获取的类型是必要(如背书被担保由

类型的节点产生,

这 。

对应不同的子部门 CA

iate intermd

之 MSP

这样的话,在各个 ,

Certs admin

这就是说,比如不同的 。

重叠 的证书路径 间没有

和 CAs

intermda 、

CAs rot

指定 需要

个部门都



例如,果该请求被 将只 per per/od

会执行 的

。 将拒绝该请求

per 并且被请求的

P , MS

定属于不同的

巳 r ,请求源头总是注 pe

很明显,因为请求源头是客户端相较于被的 。

事件注册请求

授权 per

中所处的地位, MSP

点是,基于请求源头在它本地 一

此种方法需要考虑的另

153

。 admin

的 MSP

成员的只能是该 MSP

来说,客户端能成为该 MSP

•! 成员服务提供者

MSP

7 章 第

与其证书

2. Admin



: 将管理成员关系的责任从证书验讨论中脱离出来 这是一种通用的实践

。 书都不同

黑名单

3. intermediate CA

将黑名单

操作,我们仅支持第一种方 MSP

当前的 。

中(也就是拉黑) CRL

中加入到 CAs

rot

, CRL

的 MSP

第二种是重新配置 。

目录中移除黑名单证书

对于本地配置,这意味着从

P 。 MS

第一种是重新配置-个不再包含黑名单证书的

intermediatecert CAs

很明显,这里有两种 。

被拉入黑名单: CA

intermda 方法去确保一个

实例手工重配, MSP

重新配置机制(对本地

P 通过

实例的-个配置升级消息)现 MSP

中 chanel

或者通过构建一个

MS 个

置一 正如前文提到的,重新配

证书从

中的证 CAs

intermda 或者

CAs rot

中 SP

M 无二的,与该

一 证书是独

admin 的

MSP

。 法,这种方更简单也不需要拉黑信任的证书

CAs ,但是 止 关于这点来说,不禁 。

这样是为了避免在两种不同类型的证书之间产生混淆 。

来说)需要定义在不同的目录 CA

CAs (相对于中间人 rot

的 TLS

与 CAs

rot 的

MSP



TLS 丰日

4. CAs

。 最好在产品中避免

实现剖析 MSP

7.2



F abric 员服务提供者,为

中的成 Fabric

是 MSP

各个基础组件提供一系列的身份认证

。 服务

目录结构

7.2.1

D msp 口

comn/lasp

MSP 成员服务提供者(

一 )是

P 可以 MS

一个 。

证书,以及用户认背后的所有密码学机制和协议都抽象了出来

将颁发与校验 MSP

。 个提供抽象化成员操作框架的组件 。

身份,以及的管理(验证)与认生成签名规则

员操作,以及兼容不同成标准与架构的互性

一个

Hy

perldg

er

Fabric

MSP 区块链网络可以被一个或多



管理



提供了模块化的成

自己定义

•!•

154

MSP

7.2.2 在

Hyperledger Fabric

源代码分析与深入解读

配置

MSP 初始化时

chanel

chanel

上启用 per

必须有独一无二的

节点

、 or 由

ID



client

、 orde

签名验证

MS

P 都

的成员管理规则表示一个团体,组织或分工

当具有相同标识符的两个 MSP

实例在系统

c hanel 原始块中被

检测

。 。

一个自签名的证书列表(满足

X .509

口一个用于表示该 MSP

标准)以构成

验证过的中间

信任惊

CA 的

X.509

这些参数推



CA

口一个具有可验证路径的 X.509 MSP 的管理员





CA 和中间

MSP MSP

X.509 :

为它们的成员保留了一个 OU

口一个证书吊销列表(

证书颁发机构(

),这

CA

个可选的 参数 标准)以构成

关注的中间 TLS

信任源的一个证书所认;中间 CA

TLS CA 的

X .509

信任源,服务于 TLS

证书列表

则是可选参数



证书

这些证书应该被 TLS



证书标准,且具有一条可验的路径(该通往信任源 一



口它们没有包含在任何 CRL



口它们列出了一个或多



MSP 的 OU 区内)

配置的组织单元(列出位是在它们 MSP

口用于节点签名的

X.509

EC

证书,对 MSP

DSA 密

钥) 。

验证参数机制而言是一个有效的身份

身份永远不会过期,它们只能通添加到合适的 阶段不支持吊销

证书结构

可以对已实例化的节点进行签名或认证,需要指定:

签名密钥(目前只支持

MSP

X . 509



除了验证相关参数外,为使

口节点的



实例,有效的身份应符合以下条件:

X.509

证书)

以及组织



X.509

口它们应符合

这是



是一

口一个自签名的证书列表(满足

MSP



s )的清单,每一项对应于个已登记中间或根

CA provide

证书

当多个组织使用相同信任源、中间

区的时候,会配置此参数

C RL

口对于该

配置的更改要求都是经过授权(例如

的合法成员应该将其包含进它们

一个可选的配置参数,举例子

口一个表示该



) 。

口一个组织单元列表,该

MSP

是可选参数



证书列表(该路径通往信任源的一个),以示

这些证书的所有者对

CA



的证书列表,用于校验

些证书应该被信任源的一个所认,中间



每个

,参数包括:







的默认情况下,身份(证书)验与签名需要指定一组参数

RFC5280

~

)都需要指定其配置,并在

的身份验证与各自

节点的启动将以失败告终

MSP

导自

r 节点及 MSP

。 orde

per

进行标识,当

时,该名称会被引用 到时,那么

上的所有节点(

TLS

CRL



证书 。

上未被报销 。

此外,现





1.

7 章

MSP

成员服务提供者

•!

155

cert .g o

cert.go

用于实现

certi

丑 cate

相关结构体



( 1 ) struct 包含一个

certifa

结构体:

type certificate struct { as 口 l.RawConte // Raw TBSCertificate tbsCertificate SignatureAlgorithm pkix.Algorithmidentifier SignatureVa lue asnl.BitSr 口g

(2 )

function

判断是否

ECDSA

算法签名的证书



func isECDSASignedCert(cert *x509.Certificate) bool

判断证书的签名是

否对应父证书共钥,不



func sanitizeECDSASignedCert(cert (*x509.Certificate , error)

解析出一个

*x509.Cert

重新生成签名

: parentCert *x509.Certificate)

工 ficate,

certifa:

func certFromX509Cert (cert *x509 . Certificate)



certifa

转换成

PEM

(cert

工 ficate,

error)

格式:

func (c certificate) String() string



x 509

.Certifca

格式的证书转换成

PEM

格式



func certToPEM(certificate *x509.Certificate) string

2.

configbulde

仁 go

用于初始化

BCSP

,以及

读取

M

SP

文件夹下的各种证书信息,初始化

( 1 ) struct msp

文件夹下的:

const ( cacerts admincerts signcerts keystore in termd 工 atecrs crlsfolder configfilename tlscacerts tlsintermediatecerts

=” cacerts ” = ” admincerts " =” signcerts" = "keystore ” = ” i ntermediatecerts “ =” crls" =” config.yaml ” =” tlscacerts ” = ” tlsintermedia tecerts "

MSPCon

且 g 信息



•!•

156

H y p e rl edger Fabric j 原代码分析与深入解读

用于对各个证书中的

OU

t 归

e

Organ

izat

进行校验:

工 o nalUitde

nt

if

工 ersConfiguat

struct {

Certificate string Organizati onal Uni tidentif ier string empty

、 yaml

: ” Cert 、 yaml

:”

ifcat

e

,omi 口

tempy

Organizto

” 、

alUnitdefr,om

” 、

type Configuration struct { Organizatio nalUnitidentifiers 、 yaml

:”

[]*OrganizationalUnitidentifiersConfiguration

OrganiztolUdefs,mpy

”、

( 2 ) function 读文

件: func readFile (f ile string)

读取

Pem

文件:

func readPemFile(file

获取文

([ ]byte, error)





str

工 ng

)

([ ]byte , error)

下的一系列证书:

func getPemMaterialFromDir (dir string)

设置

BCSP func



配置

( [] []byte, error )



SetupBCCSPKeystoreConf ig (bccspConf ig *factory . FactoryOpts, string) *factory.FactoryOpts



LocalMsp

获取校验

MSP

配置

bccspConfig *factory.

MSP

FactoryOps

,工

string)

(*msp.MSPConfig, error)

配置:

func getMspConfig (d ir string, MSPConfig, error)

取分层的

MSP

配置

ID string,

sigid *msp. Signdet

(*msp.

工 tyinfo)



func GetidemixMspConfig( di r string)

(*msp.MSPConfig , error)

3. idmixmsp.go 身份混合器

MSP

。在某些使用情况下,加密协议来保护户的隐私权非常重要

因为这些协议在签名、认证和传输属性的过程中都是如此

Identity Mixer

D



func GetVerifyingMspConfig(dir string , ID string)



工 r

配置:

func GetLocalMspConfig (dir string, ( *msp . MSPConf ig, error)

获取

keystorD

实现的,该、混合器具有类似于标准

。 X.509

证书所保的信任模型和安全性



Fabric

中,这是通过



证,但其底层加密算法可有效提供

高级隐私功

4.

能,如“

7 章

MSP

成员服务提供者



不可链接性”和最

小属性披

157





identities.go

包括了

signdety

实体,代表了一个

识符等,还包括了创建

sign

msp n gidenty

身份实体

,其中包括

一系列证书以及标

的实例化方法以及签名等



( 1 ) struct 定义

identy

实体所包 含的属性



type identity struct { /工 d contains the identifier (MSPID and identity identifier) for this instance id *Identityidentifier

II cert contains the x.509 cert *x 5 09.Cert 工 ficate certif

工 cate

that signs the

publ

工 c

key of

th

工 s

l 口 stance

II this is the public key of this instance pk bccsp.Key II reference to the MSP that "owns" msp *bccspmsp type

s 工 gnidety

th

工 s

identity

struct {

II we embed everything from a base identity ident ity

II signer corresponds to the object that can produce signatures from this identity signer crypto.Signer

(2 )

function

签名如

下:

func (id *signingidentity) Sign(msg [)byte )

([ )byte, error)

5. msp.go 定义了一系列 t 归

e

MSP

相关的接口:

MS

PManger



M

SP



Identiy



S ignldety



MSPManager i nterface {

II

I dent

工 tyDesrialz

interface needs to be implemented by MSPManager

IdentityDeserializer

II Setup the MSP manager instance according to configuration information Setup(msps [) MSP) error II GetMSPs Provides a list of Members hip Service providers GetMSPs() (map[string)MSP, error)

仅供书商业用途或交流学习使

158

•!• Hype



dger

type MSP 工肚

Fabric

源代码分析

erfac

与 深入解读

{

II IdentityDeserializer interface needs to be implemented by MSP 工 dentiyDsralz

II Setup the MSP instance according to Setup(config *msp.MSPConfig) error II GetType returns the provider type GetType() Prov 工 derTyp conf

工 gurat

工 on

information

II Getidentifier returns the provider identifier Get

()

工 dentifr

(str

工 ng

error) ,

II GetSigningidentity

retu 口 s a signing identity corresponding to the provided identifier GetSigningidentity(identifier *Identityidentifier) (Sig 口 i 口 gidenty, error)

II GetDfaulSign

工 ngidet

GetDfaulS

工 gni

工 ty

retu

工 dentiy()

(Sig

口 s 口

ingde

the default signing identity 口 t 工 ty, error )

II GetTLSRootCerts returns the TLS root certificates for this MSP GetTLSRootCerts () [] []byte II GetTLSintermediateCerts returns the TLS intermediate root certificates for this MSP GetTLSintermediateCerts ()

[] []byte

II Validate checks whether the supplied identy

工s

valid

(id Identity) error

飞 Talidte

II II II II

SatisfiesPrincipal checks whether the identity matches the descr 工 pt 工 on supplied in MSPrinc 工 pal . The check may involve a byte-by-byte comparison (工 f the principal is a serial 工 zed identity) or may requ 工 re MSP v alidation SatisfiesPrincipal(id Ident ity, principal *msp.MSPPrincipal) error type Ident

工 ty

interface {

II II II II

ExpiresAt returns the time at which the Identity expires If the returned time is the zero value, it implies the Identit y does not expire, or that its expirat 工 on time is unknown ExpiresAt () time.Time

II Getidentifier returns the identifie r of that Getidentifier() *Identityidentifier ident

工 ty

II GetMSPidentifier returns the MSP Id for this instance GetMSPidentifier( ) string

仅供书商业用途或交流学习使



II II II II 飞 Talidte

II II II II II II II II II II II II II

7 章

MSP

成员服务提供者

〈-

Va l idate uses the rules that gover 口 this identity to validate it . E.g . , if it is a fabric TCert implemented as identity, validate will check the TCert sig 口 ature against the assumed root cert i ficate authority. () error GetOrganizationalUnits returns zero or more organization units or divisions this identity is related to as long as this is public information . Certain MSP implementations may use atr 工 but es that are publicly associated to this identity, or the identifier of the root certificate author 工 ty that has provided signatures on th i s certificate. Examples: - if the identity is an x . 509 certificate, this function return s one or more string which is encoded in the Subject's Dist 工 nguished Name of the type OU 啕 TODO: For x 509 based identities, check if we need a de 工 cated type for OU where the Certificate OU is proely 口 amespcd by the

signer ’ s identity GetOrganizationalUni ts () [] *OUidentif ier

II Verify a signature over some message using this identity as reference Verify(msg []byte, s 工 g []byte) error II Serialize converts an identity to bytes Ser

()

工 alize

([]byte, error)

II Satisf 工 esPrincpal checks whether this instance matches II the descript 工 on supplied in MSPr 工 ncipal. The check may II involve a byte-by-byte comparison (if the principal is II a serialized identity) or may require MSP validation SatisfiesPrincipal(principal type S 工 gn

工 ngidety

*msp.MSPrinc

工 pal)

error

interface {

II Extends Identity I dent

sn

qdf

S

e

e1

飞,







←I』

t

S qd

←」

r Jfpl q4m a eft se nsh my ] b y •L g [ b 工

/

工 ty

er r or )

e

II GetPublicVersion returns the public parts of this identity GetPublicVersion() Ident

工 ty

6. mspimpl.go 包含

bcspm

p 结构体,实例化方法如下

func NewBccspMsp()

: (MSP , error)

仅供

1

商业用途或交流学习使

159

•!•

160

Hyperledger Fabri

上面的代码是在

c 源代码分析与深入解

msp.go

i卖

中的 MSP

接口的具体实现 。

7. mspmgrimpl.go 在 msp

理者

.go 中的

MSPanger 接口的具体实现如下代码所示,是

MSP 实例的一个管

。 type mspManagerimpl struct { II map that contains all MSPs that we hav e setup o r otherwise added mspsMap map[string]MSP

II error that might have o ccurred at startup up bool

8. msp/cache/cache.go 使用了 github.com/lanrpe

性能

中的 lru

cahe 缓存,以此来提高



9. msp/mgmt/deserializer.go 对 loca

t 归 e

和 chanel

desrialz 的支持接口如下:

Des er 工 alizersMng 工 nterfac { Deserialize(raw []byt e ) (*mspproto.Serializedidentity, error)

II GetLocalMSPidentifier returns the local MSP identifier GetLocalMSPidentifier() string II GetLocalDese r ializ e r returns the local identity deserializer GetLocalDeserializer () msp.IdentiyDral 工 z e r II GetChannelDeserializers returns a map of the channel desr 工 GetChannelDeserializer s () map[str 工 ng]msp IdentityDeserializer alizers

10. msp/mgmt/mgmt.go 从指定文件夹获取 LocalMsp ,并进行初始化:

func LoadLocalMsp(dir string, bccspConfig *factory.FactoryOpts, mspID

获取 Loca

lMsp

str

error

工 ng)

实例:

func GetLocalMSP() msp.MSP

msp

/ mgt

/ mgt.o 主要是对

msp 文件夹下的其他方法封装



11. msp/mgmt/principal.go princal.go

根据不同的角色获取相应实例

用于 MSP

实例获取 。

。 如果角色不存在,则返回零值并相应的

仅供非商业用途或交流学习使

ero 信息





func

(rn *localMSPPrincipalGetter) Get (ro le string)

12. locamsp/igne SignatureHd

,包括签名的实体

creato

(s *rnspSigner) NewSignatureHeader()

签名消息 func

MSP

成员服务提供者

•!

( *rnsp. MSPPrincipal, error)

仁 go

用于签名,构造 func

7 章

和随机数

(*cb.SignatureHeader, error)

: (s

*rnspS

工 gner)

Sign(rnessage

仅供

[]byte)

二 |二商



用途或交流学习使

noce:

( []byte, error)

161

·-·· EB

””-

..... ....····· .... ..... .... .... --



u ·I旱

b机 u

-

5 …

h e

u

「’

uv …

’ -

刷牙

vhu

-

QU

Gosip

Gosip

节点间的流言萤语

协议是分布式系统用于保证一致性的 一

种信息(包括区块)的手段

种方式,也是在 Fabric

中传递各



Gosip

8.1

协议原理解析

HyperLedger Fabric 通过把工作节点分解为执行交易(背书和提)排序

点来优化区块链网络性能、安全和可扩展 。

可靠 、

用了

可扩展的数据分发协议来保证完整性和

这种解祸网络操作的方式需要 一



Gosip 数据分发协议

协议(

节点利用

。 为了满足这些要求,

Fabr ic





Gosip

8.1.1

致性

个安全、

G osip

Gosip 来以 一

protocol) 种可扩展的方式广播账本和通道数据 。 Gosip 出来的消息是连

续的,并且通道上每个节点都在不断地接收当前来自多账本中已达成一致性 数据 。 每个通过 Gos ip 传输的消息都会被签名,因此由拜占庭节点发送伪造将

很容易地被识别出来,而且可以防止将消息分发到不希望送的目标处 。

迟 、

网络分区或者其他原因的 影

节点因为受到延

响导致缺少部分区块的情况,最终将通过联系已拥有这些

缺失的区块节点方式,与当前账本状态进行同步 。

基于 Gosip

的数据传播协议在 Fabric

网络上执行 三

个主要功能 :

用的成员节点并最终监测离线状态方式,对发现和通道中进行管理

1 )通过不断识别可 。

2 )通过道中的所有节点来分发账本数据

点来标识缺失的区块,并通过复制正确数据进行同步

。 任何数据未同步的节点都可以通过道中其他

仅供非商业用途或交流学习使



3 )通过允许点对状态传输更

〈-

i吾 萤

言 节点间的流

Gosip

8 章 第

163

。 新账本数据,使加入连接的节点快速得到同步

的广播由节点接收来自该通道中其他消息,然后将这些转发

Gosi p 基于

为了传播新区块,通道中的领导者节点从排序服务拉取数据并 。

和当前最新状态同步

。 消息

Gosip 向其他节点发送

节点也可以主动拉取消 。

循环重复这个操作,使通道中成员的账本和状态信息不断保持 。

, 而不是等待消息发送 息

的常数 置

这个节点数是可配 。

到通道上的多个随机选择节点

ip

( Gos 输

8.1.2 Gossip 消息传

messaging)

在线的节点通过持续地广播“活跃”消息来表明它们可用性,每条都包含公钥 的 PKI) 基础设施(

)授权的签名 CA

恶意节点无法伪装成其他,因为它们缺少根证书颁发机构( 此

的,因

由于“活跃”信息是通过密码学算法进行签名 。

中被删除 表

点最终将从通道成员身份列

节点通过收集这些活跃消息来维护

个特定的节点收到活跃消息,那么这“死亡” 某

如果没有节点能从 。

道成员身份

。 和消息发送者对的签名

ID

。 密钥



。 性,包括对节点崩溃的容错

整 本保证数据的一致性和完

享 的数据传播,因此该流程可以靠地为共

Gosip 于

基 由于不需要固定连接来维护

。 态

异时修正自己的状 差

每个节点不停地从通道中的其他提取区块,以便在出现 。

状态

在每个通道上同步节点间的世界 会

除了将接收到的消息进行自动转发外,状态协程还

。 信息

享 个通道上的节点不能在其他任何发送或共

一 由于通道之间相互隔离,

尽管任何节点都可能属于多个通道,但是过将基订阅的机制作为消息分发策

。 略,节点无法将被分隔开的消息传播给不在通道中 "'

-

。 配的证书进行身份验

与 话将

点 这样本质上使每个节点与相连的

。 成员身份绑定

Fabric

。 与网络和通道中的成员身份关联起来

s si p 之服务组件 Go

8.2

8.2.1 在本节中将分析

。 服务的组件与基本功能

Gosip 在这部分文档中,我们将要介绍

分析 prots/gi Gos

ip

的 模块中的消息类型

仅供非商业用

途或 交流学习使用

的 中

当节,点第一次连接到通道 。

点的成员管理服务提供者进行

讲解



账本

2 )认证过程由节 。

中的区块由排序服务进行签名,然后传递给通道领导者节点

。 体现

书 证

TLS

i p 层的身份认证会通过 Gos

节点在

分 CA

节点通过其由 。

层处理,不需妥签名 TLS

1 )点对消息的安全性由节 洼

进行认证,从而



候,

TLS



•!•

164

Hyperledger F a bric

1. Gosip 在

源代码分析与深入解读

中涉及的消息类型

pr ots/gip

的文件夹下主要有如:

rnessage.proto rnessage.pb . go ex tens 工 ans.go

以及其他一些测试文件 。 mesag.prot

中定义了两个

mesag.pb .go

g 叩

是使用 goprtbuf

G osip gr pc

。 。

Gosip 服务



服务所使用到的消息 。

服务处理消息

服务和包含的消息类型

/定义了一个

Gosip

生成的相应代码文件

原型方法的拓展,用于辅助 定义的

c 服务,以及

mesag. rpc

extnsio.g 则是

我们主要分析一下 mesag

pro to

其中包含了两个

类型

.pr

Gosip

ot 中

中包含的内容主要如下:

服务

service Gossip {

II GosipStrean 是用来发送与接收信息的 grpc stream rpc GossipStream (stream Envelope) returns (stream Envelope) II Ping 是用来探测

r e mote per

{}

的存活情况

rpc Ping (Empty) returns (Empty) {}

II Envelop

包括了一个序列化的 GosipMeag

/以及对该信息的 一

信息

个签名

/它也可能包括一个 SecrtE

nvelop

( 一个序列化的 Secrt)

message Envelope { bytes payload =工; bytes signature = 2; SecretEnvelope secret_envelope

II SecrtEnvlop

是 一

个序列化的

=

3;

Secrt 以及对该

Secrt 的一个签名

message SecretEnvelope { bytes payload = 1; bytes signature = 2;

II Secrt

/当

是一

remot

个可以从 Envelop

per 接

收了

中 被忽略的实体

Envelop 且不应

message Secret { oneof content { string internalEndpoint

II GosipMeag

定义了在

=

1;

该知道

Gosip 网络中传送的消息

message GossipMessage {

仅供

二 |非 商 业用途或交流学习使

secrt 的内容时,

per 就将

Secrt



忽略



8 章

Gosip

/主要用于测试,但将来可能会确保消息传递 ui



= l;

nonce t64

/消息的通道 /一些

GosipMeag /

但另

可能会将其置为 一

nil



因为它们是跨通道的

些也可能不会

bytes channel

=

2;

enum Tag { UNDEFINED

= O;

EMPTY ORG ONLY

= 2;

CHAN ONLY

= 3;

=工,

CHAN AND ORG = 4; CHAN OR ORG



决定哪些

pers

= 5;

可以转发消息

Tag tag = 3; e

JthqdRR tmMEr trs·11 oaeOf cOMvbb neeee neshh /’/’ ’ P leee sap ‘ ’1mm Alee g = Rds sme aup ee ., -mmem MM r er qas - = pb·’”’ 回贝

14eo

vt

--

AU 叶加

bess 、

q

s





=

e

-

S

II

m



p

Contains a ledger block

包含



个账本区块

DataMessage data_msg = B;



&

·。

1J14

川出



H川

hddd

ot ltse

saaa

事巾 piep 卜h elDRU

1J1 i p-- ueaaa Joaaa a GDDD Hqdqd eeua - -- 9 sttt .,==• /’/

田 1在

nu

qdq

『 dru

iep aaa 一一-

←L

set

‘『

咱4 14

←」←

--

句,“

e



G

L

L

/ 空信息 , 用来 ingp Empty empty = 13;

II ConEstablih

用来建立连接

ConnEstablish conn = 14; /用来中继关于

sta

te

的信息 =工

Stateinf o state info /用

来发送

Stae

工 nfo

mesag

5;

集合

StateinfoSnapshot state_snapshot = 16; S taeinf Staer

/用来请求 口

foPulReqst

o Snapshots state_info_pull_req = 17;

节点间的流言董

i吾

〈-

165

•!•

166

源代码分析与深入解读 Fabric

dger 出

Hype

集合 blocks

请求 per

remot /用来向

RemoteStateRequest state_ request 集合 blocks 发送 per remot /用来向

18;

=

RemoteStateResponse state_response 的意图

lead r 成为

per /用来表明

LeadershipMessage leadership_msg

Peeridentity peer_identity Acknowledgement ack

= 20 ;

ifcate cert

的 per

/用来学习一个

= 19;

= 21;

= 22;

/用来请求私有数据

RemotePvtDataRequest privateReq

23;

=

/用来回复私有数据请求

RemotePvtDataResponse privateRes

=

24;

/打包将要分发的私有数据

/在背书后的私有读写集

PrivateDataMessage private_data

= 25;

)

。 下一节是对上述各种消息类型的详细定义,在此处不赘

。 型

与发送方式 Tag

消息类型对应的 2.

中。 Org

实际上就是同一个

pers remot

资格接受该消息的

end

,顾名思义在准备好消息后也会指定需

to end



, 然后将消息发送出去,不存在随机性 pers

remot 要接收的



Tag 下面我们先总结一些各种消息类型对应的 发送方式与 gosip

后面我们再对

( 1 ) AliveMessage 口发送方式



Tag:

。 EMPTY



pers 一些

中随机选择 pers

remot 。

方式具有随机性

g osip 去,所以

字段,选取出有 Tag

,然后从

end

to end

发送方式做详细的介绍

与发送方式以及每种的使用场景, 。

某些消息类型 。

to end

end 种是

方式的特点就在于会根据待发消息 ip

gos 的方式发送,

gosip 通过

一 ,另

gosip 中有两种消息发送方式,一是

Gosip 在

Org 一个

与同 nel

cha 发送范围被限定在了同一个

的 mesag

,则

一 ORG AND

削_ CH

的 mesag

的发送范围,比如若 mesag

字段即控制了 Tag

字段,而 Tag

包含了一个

中都 mesag

gosip 的

每个发送 服务中有多种消息类型,

Gosip 前面我们介绍到,在



网络中将会传输的各种消息类 Gosip

文件我们可以了解到在 sage.prot

me 通过



消息发送出

Tag

中,

盖 言

节点间的流 Gosip

8 章 第

167 令

i吾

gosip: 。

。 Msg

A live 发送

向外 方法周期性

- periodcalSnAv ( 2 ) MembershipRequest 。 EMPTY Tag: 口

口发送方式: d

to end:

- chanel



en 。

「 shipReon Memb

。 EMPTY

Tag: 口

: 发送方式



。 per

dea 尝试连接

周期性 方法

- periodcalRntTD ( 3)

。 发送成员视图请求回复

per remot

方法中向目标 sendMmRpo

:在

0 end to end

( 4 ) DataMessage 。 CHAN_DORG Tag: 口

口发送方式: 他 方法中构造区块后分发给同组织其 cks Blo ver Deli

: 在 gosip



( 5 ) GossipHello

: 发送方式



。 织内区块同步时)

机制做组 Pul

: CHAN_AND_ORG (利用 Tag



) 。 时

图交换 机制做视

Pul

EMPTY (利用 Tag:



to end end



。 pers

remot 方法中发给特定的

Helo :在

( 6 ) DataDigest

: 发送方式



。 组织内区块同步时)

机制做 Pul

CHAN_AND_ORG (利用 Tag:



。 做视图交换时)

机制 Pul

EMPTY (利用 Tag:



to end end





p 巳 er remot

给特定的 中回复

法 方

SendDigst 在



( 7 ) DataRequest 。 组织内区块同步时) 机制做 Pul

: 发送方式



。 做视图交换时)

机制

CHAN_AND_ORG (利用 Tag:



Pul

EMPTY (利用 Tag:



to end end







方 SendRq

。 pers

remot 法中发送给特定的

( 8 ) DataUpdate 0 Tag: EMPTY ( 利用 口



。 方法周期性向外界请求成员视图

- InitaeSyc

pers botsrap

与 pers

a nchor 尝试连接配置的

, 或更新时

化 置初始

Tag

:

Pul

CHAN_AND_ORG ( 利用

时) 图交换

机制做视 Pul

机制做组织内

。 区块同步

时-)



pers



•!•

168

Hyperledger Fabric j 原代码分析与深入解读

口发送方式:

end to end : 在

0

Sen dRes

方法中回复给特定的

re mote per



( 9) Empty 0 Tag

: 不包含任何内容 。

口发送方式:通过调用 grpc

标准库中的 Invoke

方法来实现 Ping

功能 。

( 10 ) Conn Establish 口 Tag: EMPTY 。

口发送方式:

0 end to end :用于 gosip

息证明自己身份

握手 。

当一个节点连接另进行握手时,发送 此消



( 11 ) Statelnfo 0 Tag: CHAN_ORG 。

口发送方式: 。 gosip :在 publishStaenfo 方法中将最新的 staelnfo Msg 散发出去 。

( 12) StatelnfoSnapshot CHAN

0 Tag: D

一 OR_G

发送方式: 。 end

to end : 在 复 sta telnfo

Ha

pul

ndleMsag 方法中调用

请求

RecivdMsag 的

Respond 方

法回



( 13 ) StatelnfoPullRequest 口 Tag: CHAN_ORG 。

口发送方式:

0 end to end :在

requstSalnfo 方法中向

remot

发送

peers

s taelnfo pul

请 求

( 14 ) RemoteStateRequest 口 Tag: CHAN_ORG 。

口发送方式 :

0 end to end : 在 缺失的区块

re q uestB locksnRage 方

法中向某一 remot

per 发送请求以

获取



( 15 ) RemoteStateResponse 0 Tag: CHAN_OR 0 发送方式

一 ORG 。



0 end to end :在 回复

handleStRqus

staeRqu

方法中调用

,向 remot

( 16 ) LeadershipMessage 口 Tag: CHAN_D

一 ORG 。

per 回复其所请求的区块

Reci vedMsag



的 Respond

方法



169

: 发送方式



〈-

i吾

言辈 节点间的流

Gosip

8 章 第

gosip: 。

。 prosal

leadrship 发送

pers remot

内的 Org

方法中,向同一个 prose

一在

。 身份

leadr 的

自己 明

, 声

leader declartion

界发送 不断向外

per 该

则 ,

leadr

g 的 Or

是 per

方法中,若当前 leadr

-在

( 17 ) Peerldentity 。 EMPTY

0 Tag:

: 。 发送方式



时 CertSo

化 方法中初始

newCrtSo :在

0 gosip

加入 信息

identy ,将自身的

。 相关缓存中

( 18 ) Acknowledgement : 发送方式 口

。 信息

T ag 需

,无 ack

复发送方一个 :用来回

Tag 口

4 唱 陆

ac 方法回复一个

R es pond 的

RecivdMsag

k 方法中调用 Ac

在 :

to end nd

。巳



( 19 ) RemotePvtDataRequest 。 CHAN_OLY

0 Tag:

: 发送方式



。 发送私有数据请求

per remot

方法中向 Requst

scater :在

to end end



( 20 ) RemotePvtDataResponse 0 Tag: 方式: 送 口发

。 CHAN_OLY

方法回复 Respond



vedM esag Reci

法中调用 方

handleRqust

0 end to end :在 。 私有数据请求

( 21 ) PrivateDataMessage 0 Tag:

。 CHAN_OLY

口发送方式:

end

服务组件

。 函数 函数,在

函 InitGospServc

同 数进一步调用

&gossipServiceimpl{ . . . },即

是对全局





工 nsta gosipServc

下执行了

o ry InitGospServcCumDlyFa

函数中,

InitGospServcCumDlyFa 件下的





InitGospServc 文件下的

gosip/ervc_. 函数中调用了

s 巳 rv 文件下的

per/nodsta.g 人口是在

服务初始化的 Gosip

整个

serv

。 的方式

Gosip

8.2.2

end 即

, sendToEpit

定发送方式为 方法中确

SendWithAck

0 end to end :在

Go

sip

服务实

例的初







口 ce

to

170 令

Hyperledger Fabric j 原代码分析与深入解读

per 的全局实例

Instace

gosipServclnta

是-

,即代表了

gosipServclm

per

类型的结构体,

下的 Gosip

服务,

gosipServclm

servic/gop_.

gosipServc

结构体定义在 gosip/

文件下,定义如: type gossipServiceimpl struct { gossipSvc privateHandlers map[str 工 ng]pr 工 v ateHndlr chains map[str 工 ng]stae.GoipSPrvd leaderElection map[string]election LeaderElectionService deliveryService map[str i ng]deliverclient.DeliverService deliveryFactory DeliveryServiceFactory lock sync . RWMutex mes api . MessageCryptoService peeridentity []byte

我们针对该定义中的各字段 per

1.

Gosip 服务的各组件进行分析



gossipSvc

gosipSvc 是

gosip_ servic

gosip.

gossip/





type

.go 文件下定义的变



文件下的 interfac

Gossip

gosipServclm

type 的 实现是

gosip.G

gosip/ ip

2 )同一接口

Gosip impl

gos 会

gosip/

服务的初始化中

/gosip_

1 )该结构体名称与上面提到的

现的结构体名称相同;

,对应

interface ,而

Gossip

结构体(注意:

结构体实现)

类型,具体是

.go 文件下的

ipServclnsta

e 的实

有多个结构体对其实现,需要明确此处使用的是哪

。 gosipSvc 组件的初始化就在

Serviclnsta go

InitGospServcCumDlyFa 函数中

实例初始化代码的上方,通过调用 文件下的

gosip/ntera.

NewGosipCmnt 函数,

置,然后调用

N ewGosipCmnt

gosip/

函数创建了 gosip. 工 p

一 / /将某 一

grpc

Gossip interfac

e 定义如下

发送到 remot

*prot.Gs

mesag 发送到所有满足

回 被认为还

SendCrita

alive

Peers() / 返 回

serv 绑定在



peers peers ... *comm.RemotePeer)

工 pMesag,



SendByCriteria(*proto.SignedGossipMessage, /返

函数

ace {

mesag

Send(msg

NewGosipSrvc

实例并把它和给定的

type

工 nterf

/将某

文件下的

gosipServclm

文件下的 Gos

函数首先读取相关配

_ impl.go

N ewGosipSrvc

type

gosip­



pers Sendr

error

工 teria)

NetworkMmbs

[]discovery.NetworkMember

被认为还 alive

并且订阅了给定 chanel

的 NetworkMmbs

[] discovery.NetworkMember /更新 per 发布给其他 pers 的 iscoveryd layer 的 self metadata UpdateMetadata(metadata []byte) PersOfChanl(com.i

/ UpdateChn

更新

工 D)

per 发布给其他 口 elMtad(m

pers 的与其

chanel-rtd stae

的 self

(]byte, chainID common.ChainID)

metadata

一 起









发送





mesag

到网络



的其他

8 章

Gosip

节点间的流言萤语

•!

171

pers

Gossip(msg *proto.GossipMessage) PerFilt

II II

接收



RoutingFler



SubChanelctior

并且返回一个

选择那些满足给定

critea

R o utingF

并且发布了它们的

chanel

peer identities PeerFil ter (channel common . ChainID, messagePredicate Criteria) (filter.RoutingFilter, error) Acept

返回

II /如果



pas /

如果

个明确只读的通道 是

false

pasThroug

,这

是 se

ap



mesag 些

true,

Jo

工 nCha

mesag



gossip layer



gosip

layer

处理

不会介入,并且这些

mesag

可以被发送回

nder

使得





Gosip

实例加入

LeavChn

使得



该 /

但是

Gosip





实例仍 不

Gosip

实例离开

旧可

能再参与进

以传



block

( orderer/common/broadcast/broadcast. go Recv(), or 由

nityCheckAdS

进行签名,这样的检查不是非要可因

类型的检查

中留下签名即可 节点 发送





成的,只要工具没问题那这里不检查也可以

sanity chCrtEnv



chCrtEnv

边的步骤中还会有多次这样 建,在

err =

chCrtEnv

ignCofTx(chrtEv



飞 r,

,首先根据命令行指定

2 点中

per

发来的原始配置信

msg

,接着开始从

)中的

(.)位于

s.bh msg,

msg

err := srv. 中抽取

•!•

204

数据



H y pe 出

d ger

Fabr



因为是配置信,



CONFIG UPDATE msg

i c 源代码分析

chdr.Type

个以

==

msg,

整理: 一

会进入 江

)分支,在此中

进行了重新

被装进

深入解读

过滤重复的配

sytem

err= bh.smProce(g

置 ,若是

chanel



ID

per

)对

channel creat



msg

)最终调用的是

orde/

su

p

ort,

ok

否则

ate



已有频道配置的,此时进入

ifok

则说明此

配置信是用于

creat

Config ( ... ) 。

这里

p

ctxm, or

der

err er/m

ultichan

据,并填补了一些

application chanel

tem



figEn

v ,





),

配置信是

p . newCha

然算是

丑 el

p.newChal

configtx

原配置数

生成一个

内 仅停

虽然是

以指针的

ap

形式传

中的值 eC

licaton

o 口 f









后面会



igUpdate(envConfi

chanel

的频道配 置信

configMaer

。 aplicton

这里

channel

,所以这里相当于







:= ctxm

err

.Propse

configMaer

ConfigUpdate( 的

口 vCo PropseCnfig

Update



中读集和写,将已有且版本相同的配置项过滤掉生成要创建 的

频道配置数据

的函数

do

newChalofigEv

igUpd Update

ProposeConfig Update configMaer

ex

( 包

这个已存在的

... ) 实际是一样

的,都是

istingChannelConfig( .. . )中使用的

configMaer 。

channel update

所调用的

... ),与 a te(

,只不过 中的

这里的读集和写可

suport.PeCnfigUda(

opseCnf

application chanel



Ou tpu tChannel CreateTx o p 巳 er

existingChannel Config( ... )中执行的是 PropseCnfig





configMaer

利用

chanel.tx

.Pr

当于验

创建

返回

chanel

ctxm

fig

类型的验证,因为后续步骤在真正

根据原有的配置数,

a plicaton

,相

con

:= ctxm.Props

err

nity

题早发现

置信

建频道所使用的

下的

和创建频道所使用的

能,通过对比配



执行

e nvCofigUpdate

( newChannelConfigEnv,



comn

来生成

NewChanlofig

建一下,若有

这里的

支去

lConfig

置中并没有改变

envCofigUpdat sa

以生成



(这个过程相当复杂),但是 配

就属于上文提及的类似



NewChan

的配置

NewChan!ofig

nfigUpdate

ok

e nvCofigUpdate

)又直接使用原





configMaer

newChalo



if

Config ( ... )依

且没有返回供调用者继续使。

是在

经历



:= p.ma nager . NewChannelConfig(envConfigUp

chanel

新配置的

留在当前函数

时也会

说明

.. . ) ;

此时跳过

/ manger.o

sy

gUpdate



. .. )中:

date ),在

绍的

若存在,

p.existngChalof(

新频道的,

p.newChalofig(

的,但





①运行 代码



支执行

. existingChannel

Config ( ... )中的一部分 ( 5 )在





:= p.manager.GetChain(channelID) 。

upd



c onfi gu p date/co n fi gu p date. go

获取了所需要的资源,也验证配置信对应频道是否存在 用于

原始

话,过滤后的配置还会

为外衣的配置信中,形成新

( 4) bh . sm . Process (msg

Process ( . .. ),在此函数中

in t32(cb.HeaderType

configMaer

含在

c hainSuport-

> ledgrRsouc

configMaer 是



存在 中

的 )的

正好也证明了后文在创建频道时还会



( newChannelEnvConfig, err: = utils.CreateSignedEnvelope( ... ) ,





新生

成的频道配

用的

置信进行签名,类型为

8 章

Gos

HeadrTyp

existngCha!of(

... )同样执行了这

ip

节点间的

流言萤

_ CONFIG



一步,且就此返回该配

per

i吾

•!

205



channel update 信

所调



( p.proposeNewChannelToSystemChannel( .. . ),将签名过的配置信 装到一个以

sytem TRANS

chanel

CTION

为频道

ID

。 要使

(6 ) 重





orde

sytem

chain

对象





chai

nSuport

creat

Aply(env

) 。 ①

scf ,



。 的

inFlt

per

发送

到这

er



Ap



接收,配





了完

验证和填充配置信

配 。

置信

文会再次生戚,在

s u po





sytem

chanel



suport



置信生成

( .. . )

chainmpl

身影,

chanel 非虚



使用的





Ac

c 巳 pt

()后返回执行器集合,这是因为



sytem







,由于是配

(或

是与

chainSuport

),经

tem



暗盒 / kaf

的排



/ chain.go

*ab.KafkaMessage_Regular

procesRgula

),具

chanel

kaf

orde

lo

的(又由于 sy



消息中,在

so

kaf

chainlmp

巳 _ R egular case

kaf

( . .. )发给 里的

chanel

息发送给

Enque

,因此这

committers, block

( . . . )的

这里注意,一步主要目的有两个:

一条消

chainSuport

processRegular( . . . )处理 ),由配

rizeAndspct



),把配置信作为

()中被接收,井进入

(env

autho

类型检验的说法所

Aply

KafkMesg

batches ,



这里需要

置值,这是不

工 mpl

的过滤器集合

也是

处理,配置信被包裹在一条

(





chanel

procesMagTBlk



mchain.go

配置信进行整理和检查



/ chain . go

应的对象,其成员

/syte

的配

写入账本之前调用执行器

/ kaf

/ multichan



&systemChainCommitt er { ... },返

( 8) support. Enqueue (msg orde

e re

空、大



sytemChainor

sytem block

是由

ord

依次进行非

a plicaton



channel / multichan/

v )中,抽取配置信并检查之后

sanity



per

orde

置填充配信对应项的值和新建

这也同时证明了上面有关

获取

只讲

置信均只有配项而没具体

return filter.Accept,

动作和包



、 configtx.NewMar 的配



过滤 。

(),即

ly(en

NewChanlofig

chanel

configMaer

orde



chainSuport

启动时,在



到了

sytem



Aply

一步才对配置信进行值的填充



()

( . . . )中生成

sytemCha

chanel



orde

)对

而直

(

具体为

creatSysmChinFl





中,我们如愿 即根据

chanel

. authorizeAndspc(fgTx



sytem ),由上面





) ,根据频

suport.File()Aymg

: =



)中,对原始的配置

获取的是

a plicaton

s ystemChainFlr

完整的

( . .

. Chanelid

的过滤器

签名的检查后,最调用



Proces

channel creat

chanel



‘ 因为



A ply

sytem

chainsuport.g

明确



获取频道的过滤器集合并对配置信进行

,获取的是



对象



独有的,

bh.smGetCain(cdr

:=

ORDE

channel creat

chainSuport

获取的是对应

filterErr

H eadrTyp

per . go

per

chainSupport, peer channel update (7)_ ,



ok

Suport

类型变为

一点是

/ configupdate

suport, 获取

中,且



chanel

/ configupdate

信处理之后,通过 ID

置信

,然后返回新的配置信

application chanel



的配



:分支,交由

( .. . )中,将配置信抽取出来后: ok 置信

:=

,因此

support . BlockCutter() .Ordered 会单独

作为





block

,且

suport



•!•

206

Hyperledger Fabric

system chanel 器集合



chainSuport

A ply(env 重新生成

(

后返回 包

息打包成

i,

block

前文所述的



/ multichan



/ sytemchain.go ),

最终执行





t ()

age

chanel



可以

看到

ge chain

Suport



④还是在

象,



Wri

chanel



orde







工作









Send(chCrtEv

的民

),在

第一步结束。





creat

err

命令行的- chanel



multi

最终效果

Ledgr





对 象成员



chains

channel update . ledgr

使用到的

. Apend(block)

的账本





srv.Send(

执行至此,

per

channel

( ... )中,定位 )就是

r channel creat



Send

orde

动作的

per

现中的

节点返回成功

delivr block

() ),

也即

节点的应答信息





动作来说,至此整个结束

至此,



/ chane

l/ create. go

),在



服务每隔 文创



excutCra(

ct)

定时限内(默认

5s

20ms

,即上

... )接

broadcstClien.

orde

getGenesisBlock (cf

Send(

getAck

中执行完

per

节点处



( ... )实

( . . .)内部已经接收到来自

0 的

Handle

... _SUCE pe

重新返回到

索要一次序号为

( ... ) 。

broadcstClien

t 指定),利用

成功获取或超时



channel update

=

chanel

ndCreathiTsco

per 。

cs

/ broadcst.g

巳 rclient.go

Send

( l 0 )开始第二步 block ,



/ orde 巳 .go



节点接收这个应答的地方是在

/ comn

per/chanlt

er

per

tem

channel ,向发起

per

newChai

chanel

而上文提及的

/ broadcst

),之

per 参看

sy

/ comn

这里注意一下,

口内部(





a plicaton

的应

块写入

口 que(msg

理完毕创建新的



集合后,

gensi

orde

s u port.E

此执行的



基本结束

( 9 )重新返回到

multiLedgr

aplicton

teBlock ( . . . )中,兑现执行器

aplicton

creat

创建的



orde 。

就是这里

,因

r.go

块)并在

对象并启动相应的服务

comiter.

stemChainor

,创建

nesi

... ),

- > scc . filter. cc. newChain ( scc .

/ multichan/

建了频道的账本(且写入配置信作为

也会把

批消

WriteBlock(

循环中依次执行

sy

Cami

orde

aplicton

chain Suport







里因为只有一个



在这里,正式创建了



的过

批消息,将

_,committer := range comiters 。



在循环中依次处理每

orde/multichansp.g

(),即将执行器集合兑现

中添加

chanel

committers[i], ... ) 在

for

configTx

sytem



在这个函数中,首先

orde

是 。

suport.WieBlck(b,

support. WriteBlock( ... )执行的是

Comit



sytemChainor

: = range batches

batch

b l ock

(



comiters

面提及的被



,然后能过

写入

获取的执行器集合

的,即上

执行 集合中只

for

账本中



)之 。

源代码分析与深入解读

建的

向 a plicaton

orde

,可由

per

节点指定的 chanel

中:通过

channel aplicton



gensi

块,

直到



( 11 )开

始第



File(file,



人文件,供

per

b,

步 064



通过

b, ),将第二

err

channel join

proto . Marshal (block)

:=

动作使用

步获取的



block





,整个



per

aplicton

ioutil.Write-

- >

channel ID+.block channel creat

动作执行完毕

的格式



•!

s ip 节点间的流言萤语 Gos

8 章 第

207

2.join

/

/ chanel per

在 。

文件 l.bock

mychane

是 ID

是一个 的

建 创

creat

channel

channel ,对应生成的即为 aplicton

的 mychanel

per 这里假设

。 等模块的服务

gosip 主要指

指账 本,服务 数据主要

。 套

的数据和服务相配 chanel

aplicton 节点中的

orde 在于

以便和对 应存 数据和服务,

地 点的本

节 per

的动作是

j oinj oin

peer channel

中: join.g

)中:

(I)joinCmd(c f ) ->join( ... )-> excutJoin(f

( spec,

(

ocatinSpe{ 川

: = &pb. ChaincodeI

invocation

prop,

- > signedProp,

err =

。 书申请

签名,形成一个背

( . .. ) , 包装+

口 edPropsal putils.GeSg

->

... }

= putils. CreateP rop osalFromCIS ( ... ) er

一’

。 块数据

nesi ge

l 的 mychane

读取的 中

mychanel.bok 从

、 cs.JoinCha

作 入参数,指定了动

执行的输 sc

作为 Chaincodelput

中的 ”,其

明书 “说



格式 pec

ChaincodeS 的

cs c 建了一个关于

: = getJoinCCSpec ()创

err

( cf.Endorse rClient.Proc essPropos al( . .. ),通过背书客户端发起 。 请求

。 赘述

书过程不再

( 2 )背

case ,根据上面所述的“说明书”将进入

)中 stub

Invoke( 的

configure.

( 3 )在 : 分支: JoinCha

: )中

cid, block joinCha(

( 4 )在

。 具体动作

的 join

),最后执行

( join Chain( cid, block



查验 证 块,然后进行一系列的检

gensi 的

mychanel 数中获取

createChain( cid, I, cb

(

着重看 个函数中,

链对象,在这 使用的

mychanel 建针对

),创



block

myc 模块中专供

gosip 地

节点之间建

频道索要

本地 per



, 创 块

nesi

.InitializeChannel( ... ),该句初始化了

节点本 per

ge

e l 的 mychan



servic.GtopS() 下



本 使用的账

mychanel 专供

根据 ),

dger(cb CreatL

( I, err= ledgrmt.

。 ,此为监控服务

建事件服务 创

: )中

ck(blo inFromBl

Creath

( 5 )在

) , nt(block

Eve produce.SnPBlk



。 地的链

点本 节

per ),初始化

. go

/ per

/ per

( peer.InitChain( chainID) ( core

。 的

生成 服务,均是在此

gosip 、

的账本 和使用

涉及 中

题的文章

主 作为

gosip 、

code

c hain 在这之前,以

。 务



g osip 链(账本)和

的 mychanel

点本 节

per ),创建

p er/p.go

( peer . CreateChainFromBlock (block ) (core/ 地的针对

个参 第二

])从 [工

( block , err := utils.GeBockFrmy(ag

务连接,由 了服

数据块并添加到本地的用于



per



服 gosip

使用的 hanel

orde 可以源不断地从

点就

mychan

el

的账本



orde

服 务与 gosip

务,并在

节点中的

mychanel

+!+

208

Hyperledger Fabric

( 6 )在

InitCha(

个函数变

量 行

pe

源代码分析与深入解读

chainID

是在

per

)中:只执



行了

点启动起来的

er.Initalz



即初始化



点的

的针对

in

ysC

per

要与

per

节点启动时部署的

完毕,

sec

7GJX.





sytem

ode

j

,也



也就绪了

如此,

per



/ star.go



这里所部署 se 凹

ID



频道

ID

,默认使用

sec

)中,当

per

a lResp





接定位回

pros

E 中执行

的链上

oinChain(cid, block

在接到背书的返回结果

中执

chainode

chaincode .命令若是没有指定频道 的

serv func(id



/n

直 。



sec

( per

,否则使用的是具体针对某

)中

t 旨

/s tar.go

链上部署的

基本结束,开始一路返回过程省略可以

excutJoin(f

~←

ID sec

sec/ cs cc/ configure. go

则背书过程



所赋的值是

节点启动时部署的

sec

chainltzer

node

上执行的过 程中所使用到 per

core/

per/

mychanel

()做区分,即如

( 7 )重回 的

sec



署了指定频道

链,其实就是在

mychanel

l 的

tS



动作在

mychane

-=E 主 1"f



,而

chainltzer

mychanel



cha inc ode

go

候被赋值的,

)},即部

per

的是

tializer( c i 哟

( ... )时被赋值的,这里给

string ) {sc.DeploySC(id



chainl

后,整个

4

点执行

/ chanel/joi. j oin

动作也



事件机制

8.6 在

Fa

关信息

bric

中,

采用事

件的

形式来通知客户端交易完成及写入区块或者智能合约





件相

Fabric

8.6.1



本节讲解在

Event

相关实现

F abric



点中如何实现事件的订阅,以及信息推送



1. events/consumer/adapter.go EventAdapr

是一

个从

fabric

evnt

服务器注册感兴趣的



件以及接收消息的



端接口: Getlnr

巳 stedEvn;

D

D Recv; Disconet

巳 d 。

D

2. events/consumer/consumer.go EventsCli 口

结构体持有

grpc

NewEvntsCli

连接的流和用于与消费者对适配器

adpter



:用于初始化一个连接某 new

per



EventsClientConnection Wi thAdres send





Event

签名后通过

D RegistrAync 的



send







异步注册对应的事件到

g 甲

:返回



c 的

stream



fabric

发送到



EventsCli grpc

连接

evnt evnt



服务器,不等待响应调用上面

服务端







个客户

。 Event

nt 。 Eve

发过来的 :建立和事件中心的连接,获取客户端感兴趣同步注册到服务并 Star 口

调用的方法,于接收服务端 gorutine

方法中重新创建一个

D processEvents : Star



- sdk

- go fabric

相似的应该是

: 使用方法

consumer.g

。 EventAdapr

( l )根据自身需求实现

。 文件

.go

「 evnts/produc

8.6.2

EventAdapr

巳 ner

b lock-ist 具体示例请参考

方法,该是阻塞型 procesEvnt

的消息,调用客户端实现 evntbu

的,用于不断接收来自

执行 gorutine

启动一个 。

设置的感兴趣事件类型

服务器的连接,同步注册一开始 evnthub

方法:建立与 Star

的 EventsCli

。 实例

EventsCli 生成

NewEvntsCli

( 3 )调用



巳 r 包下的文件 evnts/produc

本节主要介绍

1. producer.go g 叩 c 的 结构体:实现

Events Serv

的结构体 Chat



2. events.go handler List

。 :事件处理器接口

type handlerList interface { add(ie *pb.Interest, h *handler) (bool, error) del(ie *pb.Interest, h *handler) (bool , error) foreach(ie *pb.Event, action func(h *handler)) : 该事件分为

。 :普通处理器列表

genricHadlLst

←」

←L

sh

r e

14

{ dxp ·-CMS t rb HU La • we gya co sr uo n lsd atm enn nea rRe ncd l e EW

中对应都有类似的实现,最为 SDK

,每个 中有使用

block-istenr.g 这个文件主要在

。 的字节数组

signer :获取

getCraoFmLclMSP

( 2 )使用



。 :终端和事件中心的连接

D Stop

信息 Event

处理服务端发来的 Events

proces ,调用

gorutine 启动一个新的



。 ()方法的时候使用

Star ,在客户端没有调用

Event 中接收

stream 的

grpc :从

Recv 口

。 方法

send :反注册客户端感兴趣的事件,不等待响应调用上面

D UnregistAyc



209

中,发送到服务端同步获取过来的 evnt

注册感兴趣的事件到 :

D regist

•! 节点间的流言董语

Gosip

8 章 第

wr

4 咱

吁--

「 L

中有

n 、

e ld



Recv

方法



•!•

210

Hyperledger Fabric

handlers 这种

源代码分析与深入解读

表示方法是

g o 语

0 chainodeHlrLst

言里实 现

set 的常用方

:链码处理器列表

法 。



type chaincodeHandlerList struct { sync.RWMutex handlers map[string]map[string]map[*handler]bool handlers 表示对应的

List

chainodel 下的

evntNam

中的特殊数学结构,因此其分为



2 个结构体

genricHadlLst 的

口 ad



:将

del



handler



:将



handler

foreach

口 ad



中移除 三



action

个实现方法



: 其中 ie

*pb

. lnters 这个 空

evntNam

参数在这 个实现中没有用到:

,将对应的 handler

添加到对应的



:操作加了写锁,判断相关参数是否为空将对应的



chainodeHlr



,执行

下对应的

del

I 是



操作加了写锁,判断相关参数是否为

chainodel





handlerList 的

bo



handlerList handlerList

chainodeHlrLst

] 。



加到



:遍历

个实现方法

map[*hndler

chainodel

handler 移除

foreach act

evntNam



:操作加了写锁,获取指定

执行



chainodel

i on ,此方法中还有

一 段代码(第

下的 l 14 ~

EventNam

159

下的所有

行),永远不会执估计

handler, 。

3. handler.go 口 handler



:用于向

new

evntHub

EventHadlr

注册客户端感兴趣的

: 创建 一





handler

件以及仅注册相应的事 。

o

4. register_internal_events.go 口 getMsaTyp



:获取消息类型 。

adinterlEvTyps :添加相应的事件类型



5. examples/events/blocl• Iistener/block-listener.go 为 Recv

adpter 和-

结构体实现 consumer.EvtAdap

Disconetd 。

creatEvnC!i

main



接口的 3 个方法是客户端自定义逻辑的实现

:调用

方法

并进行相应的输出





GetlnrsdEv

ntC!ie

生成

, 没有事件返回时进行阻塞

EventsCli 实例,并调用

; 有事件返回时则解析,

Star



8.6.3 本节将介绍在

Go SDK GoSDK

中 Event

相关实现 中如何实现客户端订阅相关的事件信息

、 -



NewEvntsCli

> creatEv

个方法:-



方法 。



8 章

Gosip

节点

间的流言茧

i吾

1. EventHub EvenHub

主要

用于



D SetPrAd

:设

D IsConectd

置事

:判

D Conect 口

册和反注相应的事件:

:和

注册

事件 g 叩 c 是否保持连接状态

件服

:和

务器建立连接 事件服务器断开连接

。 :注册

D UnregistChacodEv

一个

ChaincodeEvt



:取消注册一 个

D RegistrTxEvn 口





UnregistTxEv



ent









:注册一个

UnregistBlockEv

。 事

: 取消注册一个交易类型事件

D RegistrBlockEv



ChaincodeEvt

一个交易类型事

B lock

类型的事件

:取消注册一个

。 Block

类型的事件



2. EventsClient E ventsCli



要用于管理事件的异步发送及连接:

D RegistrAync



异步注册需要的事件

D UnregistAyc





异步取消注册相应



事件



3. EventHubExt EventH

ub

Ext

用于对

SDK

添加额外的扩展功能



4 . ChainCodeCBE 在

EventHub

内部使用,户持有

chainode

ChaincodeEvt

5. ChaincodeEvent 相

关信息的封装



evnt

。 。



D RegistrChancodEv



地址,用于指明在哪个节点上注册该

断客户端与事件服务的 事

Disconet



的注册回调





•!

211

........ .... .... .... ..... .... .... .... .... · --EE

··········

·a··· · ·· ····

ι i'v-~.tc

9





9



BCSP

加密服务提供者的设计与实现

BCSP

为加密服务提供者,

Fabric

网络信息传输提供了密码学的保障



密码学相关知识介绍

9.1

安全

9.1.1

基础

当在互联

网上

的地,正

是因

交换数据

时,

为如此

数据在

,安全技

被传输的数据会经

过各种样的设备和网络后才能到达目

术在



网络上传输的过程中,

( 1 )当

显得

存在着 A

intercpo

联网时代才

想要向

B

如此

4 个比较

重要

显著

发送消息时,可能存在第



的问题:

三方

X

来截获消息的内容,这种问题称为



( 2 ) 即使 也

存在

B

A

本来要

将消息发 送给

被称为

相信接收到的消

B

息来自于

spofing

A

,也存在

X

,但实际上是

伪装成

B

X

来接收消息的可能性;同样,

伪装成

A

来发送

这条消息

,这



( 3 )假设

A



B

发送的消息已经完成了,也存在过程中

X

的可能性,这种问题被称为

fa l s i ficat

i on



篡改消息

内容

除了有意篡改消息以外,也存在发送的过

程中因为传输设备出现异常而锚的可能性



( 4 )假设

B



经正确

地接受到了

否认了这条消息是由

他 业



签署合格的媒

当然,上



种问题



介,这种问

4 个问题并不仅

A

自己发出

的, 题被称为

发送的消息,但是方

当出

A 现这种 情况

存在于人与

repudiaton

时 ,互

联网将无 法成为商

业交易或商



人之间的信息交换

出于自己的私心考虑,



同时存在于我们日常上网



的过程中



下面我们来简单介绍



213





采用消息验证码或者数字签名技术





•!•

BC CSP D 日 密服务提供者的设计与实现

下解决上述问题的安全技术

为了解决消息被截获的问题,我 术

9 章

可以使用加密技术;解决第二个问题的方法是 解决第



个问题也可以使用消息验证码或数

数字签名技术还可以用于解决第四个问题

字签名技

。 其实数字签名本身也存在问题,就是它无法验证使用的公钥拥有者真

性,所以就出现了数字证书此来解决

字签名本身的

问题

加密基础

9.1.2 为什么在现代的互联网世界中我们

需要

息到达



B

加密技术呢

?例如

A

需要



B

发送消息,在

获,因 此

之前会经过各式样的设备和网络,所以有很大可能消息被恶意第



出于这种原因,在发送消息之前对加密就显得非常



被加密的数据称为秘文,消息将会以形式发送给

必要了



B,

文后,将其解密就可以获得原来的消息



称为解密



B

收到

A

发送过来的秘

将加密的数据还原成始状态过程被

当你将数据以加密的形式发送出去时,就可不必担心恶意第



的数据了,正因为如此加密技术在现代社会中才显得重



下面我们来仔细了解一在加密过程中涉及的操作



据的类型是什么,计算机都将它们视为

方所截

一系

方截获到你



我们知道,在计算机中无论数

列二进制数据,也就是一长串的

0 和

所以,尽管存在各种样的数据格式例如文本件、

音 所有的数据都是以二进制形式行管理

乐文件和影像



l 的组合



在计算机中,



同样,在加密的过程中我们也可以计算机管理数据方式来思



据就是一系列有意义的数字,虽然密文同样



是一串数字,但由于它



机并不能正确地解释它们,加密就是通过计算来将原对于



转换成计算机无法解释的数据过程

。在 这个密钥向样也是由数字组成的



我们的数据



的随机性,计算

机来说有特定含义的

加密的过程中,存在





数据

个用于执行计

算的密钥。

换一句话说,加密就是通过钥来进行数值计算将原

本可读的数据转化为不过程

。 不可读的数据转化为过程

反过来,解密就是通钥进行数值计算将



我们

通过一个“异或”的例子来理解加密和程,

XOR

值表决定的,我们有

(异或)的结果是根据真

: O XOR 0 = 0 ; 1 XOR 1 = 0; 0 XOR 1 = 1 ; 1 XOR 0 = 1 。 “异或”操作的 B



“异或”的结果,那么你就可以通过

个特点就



A

XOR B C

另外一个数字



或”操作得到秘文,同理将生成的与私钥再进行



C, B XOR C

A

=



B

A

,这意味着如果

C



A



中任意一个值进行“异或”操作来获得

将这个知识点用于加密和解的过程中,我们可以私钥数据进行“异

一 到原来的明文

=

次异或操作,我



就可以还原得

从上述过程中我们可以看出,相同的私钥被用于“异或”加密解数据





214



Hyp



dger

Fabric

源代码分析与深入解读

晗希函数

9.1.3 一个哈希函数可以将不同长度的据映射为定值



我们可以形象地将哈希函

数理解为榨汁机,我们将原始材料(的据)放进晗希函得到饮哈 值)

。 料



我们可以将原材料转化为饮,但是反过来并不能通一定的操作 类比我们的哈希函数,可以将原始据通过晗操作转化为值但是不

可以使用哈希值得到我们的原始数据



也就是说,哈希一个不可逆的过程



就是一个数值,但我们常使用十六进制来表示

哈希值其实

。 哈希函数的特点如下

: 第一个特点就是通过哈希函数得到的输出长度都固定



度固定是针对特的哈希函数而言,例如

SHA

字节,这就意味着

即使你的输入数据非

依然是固定的

20

-1

哈希函数得到的晗值长度都是

20

常大,但是通过

字节







SHA

似的,不管你输人数据有



-1

哈希之后,你得到的输出

小,得到的输出依旧是

20

字节



哈希函数的第二个特点就是在相同情况下,输入永远得到 出



第三个特点也是非常重要的一,就你输入两类似数据即使只

相差

1 bit 输出



,得到的输出相

差也会非常大



哈希值不会因为你的输入数据相似而得到

的情况

第四个特点就是可能会存在输入的数据完全不同,依旧得到两相哈希值 。

这种情况出现的概率非常小,被称为哈希碰撞



第五个特点就是哈希函

数具有单向性或者说是不可逆,你几乎能通过哈希值得到原始据这也函

数与加密之间一个非常重要的区别

。 非常简单

晗希函数的最后一个特点就是计算哈值过程可以

。 我们可以使用

M

常用的是

SHA

-2

D4



M

D5



SHA-0



SHA

- 1,

SHA

- 2 之类的哈希函数,现实生活中最



9.1.4 共享密铜加 共享密钥加在和解的过程中使用相同私

。 假设

A

想要通过互联网将一段消息发送给

B ,在发送给

同的网络和设备



易截获该消息



但是,如果

A

B

直接将消息以明文的形式发给

的过程中消息经了很多不

B ,



恶意第



方就非常容

正是因为这样,所以在传输消息的过程中通常需要经加密处理



钥,将明文加密为再进行发送



B

利用相同的私钥

利用私

将接收到的密文解得原



以,经过加密处理后即使恶意第三方截取了消息在不知道钥的情况下他也很难

道原文是什么

。 利用相同密钥进行加和解是共享的一个特点,常见方法

有凯撒加密、

AES



D ES



O TP

等,其中

AES

使用最为广泛



当然,共享密钥加本身存在着一个致命的缺陷:假设

B 三

来没有直接触过,

送的过程中,密文也可能被恶意第 B

可能并不知道

方 A

X 用于加密的钥,所以在这种情况下

所截获



现在存的问题是,由于

收到了

A

发送的密文,在 A

和 A

B 就需要某

可能从





种机制把密钥传输给

B ,就像 B

利用



接收到的来自

X

A

A



B

9 章

BCSP

加密服务提供者的设计与实现

传输密文一样,

A

•!

同样需要在互联网上向

的密钥就可以对文进行解

B



同样有机会截获到密钥,这一来

X

215

传输密钥



既然是在互联网上传播,恶意第



就可以用截获到的密钥对文进行解



在上述过程中,我们可以清楚地看到密钥本身的传输存问题所

A

可能会想通

过对密钥本身进行加后再传输,于计算机来说一个仅是段数据所以我们可

以使用新的密钥对老进行加井生成文,再传输



要一个机制来传输新的密钥,这样就会陷入



个循环,总结



一个安全传输密钥的机制,这问题被称为分发

。 使用

Dife-Hlman

9.1.5

下,共享密钥加系统缺乏

解决密钥分发问题有两种方法:

密钥交换这样的协议;使用公加算法



公铜加密

公钥加密与共享



大的区别在于公钥加密和解时使用是不同

钥,用于加密的被称为公(

public

key

),用于解密的钥被称为私(

private

除了使用不同的密钥之外,公加在和解时比共



常用的公钥加密有

RSA

加密和

下面我们来了解



假设

A

想要给

EC

B B 。

到原始可读的消息

。 即





发送一段消息,首先接收者 B

) 。

。 B B

后的密文发送给

加密(椭圆曲线)

key

密钥加需要花费的时间更长

下在公钥加密的情况数据是如何双方之间交换

一个私钥,公部分将由

文,所以

但是我们现在又需

发送给

A,

A

使用从

B

会生成



对密钥,分别是一个公和

利用只有自己拥的私钥将从

发送过来的公钥将消息加密,并 A

发送过来的密文进行解,从而得

使恶意的第

由于只有公钥和秘文在网上传输,而且通过并不能解密 三

方截获了这两个信息,他也并不能解密文得原始消



公钥加密很好地解决了共享中私分发的问题

所以,

。 使用公钥加密的另一个好处就是,在



一个例子,假设

B

个不确定人数的组织中交换据非常方便

已经准备好一对密钥,即

。 一

己的公钥并不会有什么影响,所以

B

个公钥和与相对应的私

。 。

B

最后将加密的数据发送给

B 。

现在有多个体需要向

B

分享的公钥,然后利用将

B

因为公布自

将自己的公钥布在互联网上,另一方面由于私

钥是绝对不能让他人知晓的,所以私需要牢地守护 据,首先这些个体需要获取由





发送数

要发送的数据进行加密,

利用自己的私钥分别将收到密文解获得原来数据



所以,在存多个体时我们并不用准备公钥还有一点就是由于只消息的接 收者才具有用于解密的私钥,所以公加安全性非常高

。 然而在存诸多优点的同时,公钥加密也着两个严重问题



( 1 )公钥加密所需要的时间相对较长,正是因为这样并不适用于交换大

数据的情景





为了解决这个问题,出现“棍合加密”样的方案



的公钥记为

( 2 )公钥本身的可靠性存在问题 PB

。 ,将私钥记为

SB

,假设存在着恶意的第



B

生成公钥和私时,为了方便起见将 三

B 方

X

,想要截获

A

发向

B

的数

生成 据,

•!•

216

Hyperl edger Fabric j 原代码分析与深入解读

同时也生成了一对公私钥,分别记为

PX





B

的公钥

PB

替换为自己的公钥

PX

钥的产生者是谁,所以

A

用公钥

PX X

PX

加密的,

该发送给

B 密文是用

B

A X

的消息

B



sx



B

PX



将消息用

B B

可以正确地对消息进行解密,

A

时,

X

B

PB

加密后发送

给 SB

B 。

由于过程中产生的

方截获过了

还是来自恶意的第

正是因为

B





问题,出现了数字证书

成功截获了本来 。



B

隐蔽

将自己的数据

对密文进行解

都无法知道消息已经被第

无法验证自己获取的公钥到底是来

X

截获了秘文,由于该是用

上述通过替换公钥来截获消息的攻击方式被称为中间人 A

A

X

可以利用自己的私钥



接着

来对秘文进行解密,这样

的公钥

时,

由于通过公钥并不能发现 。

sx ,所以

PB

A 。

B X

发送自己的公钥

发送给

将加密后的消息发送给

接着,

A

的公钥有没被第三方调包

可以用自己的私钥

的公钥加密



,并将

并不能发现

进行加密,当

的公钥



这种问题的关键就在于

三方

X 。

为了解决中间人攻击



混合加密

9.1.6 如果使用共享密钥加,我们会遇到分发问题



如何安全地分发密钥?另一方

面,如果使用公钥加密漫长的解过程又降低了信息传输效率?昆合出现 就整合了两者优点,规避的缺

。 混合加密利用共享钥

加解密的

安全性

高效性的同

时,使用公钥加密来保障共

享密钥的



假设

A

要向

B

发送消息,可以利用共享加密方法进行高效



密钥也同样被用于解,所以

A

需要把密钥安全地发送给

B 。

式,通过公钥加密我们可以安全地将发送给

B 。 个公钥和一私,

B 加密后的钥再发送给

将公钥发送给 B,

A, B

利用

B

B 清楚

密的快速性,混合



9.1.7

B



看到

1 昆合

密已经被应用到



的公钥对称密进行加处理,

就可以准确快速接收到原始数据 加

密的高安全性和共享钥

中,用于互联网上安全交换数据



antheic

ation



falsict

ion

首先,我们来认识一下为什么消息验证码有存在的必要 向

B

发送自己要购买的商品编号

xyz

A

,然后

A

先使用某种安全的方法将共享密钥



密钥交换方法可以是公加还

Dife 要发送的消息进行加密,这里

detcion

。 A

使用了共享密钥,



消息验证码 :

的消息就

就利



密整合了公钥

SL

消息验证码实现了两个功能

件商品,

自己生成一对密钥,即

现在剩下唯一要做的就是将经过对称密钥加后

B ,由于共享密钥加解速度快 们可以

接收者

A

B 。 上述过程我

其实密钥也是数据的一种形

利用自己的私钥对加密后进行解处理,这样

用公钥加密将安全地发送给 文发送给

A

密操作,因为用于加的

是我们需要发送的商品编号,加密后得到文被

- Helman

密钥交换手段

假设



A

需要从

B

那里购置一

将该商品编号加密,假设时

密方法使用的钥传递给 。

B ,这种 A

使用共享密钥对需

密解



发送给

B,

B

9 章

•!•

BC CSP D 口密服务提供者的设计与实现

得到密文后使用共享钥进行解明,即

A

发送的商品编号



传输过程看起来好像并没有什么问题,但是现实生活中可能发这样的事情:当

A

送经过加密后的文时,恶意第





X

在传输过程中将密文篡改



文并解密后,得到的商品编号却变成了“

123 将商品“

123

”而不是“

xyz

”发送给

”,而

A 。

217



B

这个



B



收到被篡改后的密

B 并不知道商品编号已经被篡改过了,

B

因为加密无非就是对原有数据进行值计算,所

以,解密的过程也可在被篡改数据上进行但是如果原消息比较长而经后 的密

文解密后变成了一些没有实际意义的消息,那么接收方可能会察觉到已经

被篡改过了

。 很难

但若是一些人类无法直接识别的消息,例如上述商品编号那么收方就

察觉出消息被篡改过了



所以,为了检测消息是否已经被恶意第



密之外再增加一些手段就变得非常有必要了

方篡改过,除了加

。 消息验证码就是为了检测有无被篡改而出现的一种解决方案



一看消息验证码是如何工作的



消息验证码的密钥,然后

A

特定



A

将加密后的文发送给

B

将该密钥以安全的方法发送给

之前,

下面我们来仔细看 A

B ,然后

A

同时生成一个用于

利用该密钥和文以

算法生成一个值,这由密钥和文组合的就被称为消息验证码通常简写



MAC



MAC



样的方法来生成

可以简单

地理

MAC 。 广泛

生成

MAC

M

值,例如

AC

A

有无被第 A

来的 。

那么如果在 在

A

A

将密文和

值,

B A 的

接下来 B

的过程中,

X A

以即使

X



篡改传输中的消

将密文和

也无

的解

释,我

传输的过程中被他人

们可以 篡改

对用

于 ,那么







X

做到让两



就可以判断密文、

者的

B

不知道用于生成

重新计算出来

MAC

M

AC

值的密钥,所

值不一样时,

值一致,所

以 B

B



重新计算

就可以清楚地知道该消息

也可以 息是由

值的密钥,如果 B

还是

B

B

伪造

或者

MAC

可以避免消息在 。

还原这样的操作, A

到,使用消息验证码

然而,消息验证码同时也存在着一些缺陷

MAC B

声称该消

清楚

密明文的钥和用于生成

并不能区分原文是由

可以

MAC



经过上面



收到以后重新计算

值同时改变从而让

?由于 MAC

经被别人做过于脚了

将密文篡改会怎么样?假设

可以选择将密文抛弃然后再重新请

MAC



X

B

B 呢

X



值并不相同,这样

在这种情况下,

值时发现与被篡改后消息一起携带的 已

则说明

B MAC

一致的可能

息,

值,

进行比较,如果两者相同

篡改了密文,然而

发送的 X



接收到以后,需要根据

MAC



是否存在

MAC

B

一样,根据密文和钥来重新生成

的过程中,恶意第

。 值和被传输的

A

B,

使用最为



值和 。

HMAC

值一同发送给



我们可以使用各种

等,现实生活中

仅需将密文解得到原

或者两都已经被篡改过了

MAC

B

CMA

MAC

B 值发送给



自己重新生成的

B

再次发送相同的密文

MAC

B

MAC





。 和

会发现自己计算的

MAC

方篡改

将密文发送给

MAC

OMAC MAC

MAC

密文并没有被他人篡改



连同密文和



发送过



HMAC

值以后,

值检测密文 这样根据

解为密钥和文组合起来的哈希值

生成的,这样

A

A



否认了该消息是由



B



享一

MAC MAC

A

A

可以加密消息和生成

同样也可以加密消息和生成

生成的,那么假设

由于

是恶意的,在

他自己发送的

A





值的

换句话说,你

将消息发送给



B

为了防止

后,

A

•!•

218

Hyperledger Fabric j 原代码分析与深入解读

这种现象出,我们需要采取另外一技术也就是下面会讲的数字签名



数字签名

9.1.8 数字签名可以保证消息的不否认性



authenico



falsicton

数字签名除了完成消息验证码实现的

检测的功能之外,还实现了让消息真发送者无法否认这条

消息是由他发送出的功能,这就不可否认性

。 在了解数字签名的工作原理之前,我们先回顾一下消息验证码



统中,为了验证消息的发送者是否真实密钥拥有会将

MAC 上



为了方便起见,这里的消息以明文形式传输

A

的密钥发送给

B ,然后再将明文和

MAC

MAC

值,然后再确认两者的

MAC

送者并且发的明文没有经过他人篡改



MAC

B, 。

值附着到要发送的消息

先以某种安全的方法将用于生成

值发送给

值是否相同

使用消息验证码的系

B

根据收到的明文和密钥重新生成

如果确认成功,那么就说明

A

然而由于消息验证码采用的是共享密钥,依旧

存在拥有该密码的组织伪装成为消息发送者可能性,比如说当

A 之后,

B MAC

却声称自己才是这条消息的创建者



值,那么当

A

与除了

B

是真实的发

把一条消息发送给

B

还有一点就是,由于采用的共享密钥来生成

之外的其他个体进行消息交流时,则需要生成一新密钥



为了解决消息验证码内在的问题,我们引人数字签名技术不采用 MAC

,而是采用只有消息的真实发送者才能产生相关数据技术这里就被称 为数字签名

。 当

A

A 。

例如

A

abc

”,

A

会生成一段只能由

A

B ,可以判断数字签名来自发送者



A ,但是他自己并不能生成这些数字签

与消息验证码相对比,由于数字签名并没有使用共享密钥所以

A

或个体发送相同的数字签名



字签名

我们知道用于生成

MAC

时,我们使用了和公钥加密系统

类似的



个过程

。 A

会事先生成一对密钥,分别是公

P 和私钥

加密自己的数据

S,

,然后再将加密的文发送给 进行解密,整个过程结束

B

将公钥

B

A,

A

使用

B

所以在公钥加密中,被用于而私解正是

才拥有私钥,故只

B

A

才有私钥,只他们能加密数据而生成的

数据可以被任何持有对应公钥的人进行解密,但这样加手段没意义



个角度去看的话,我们就可以理解为密文生成者必定是持有私钥

A 下面我们来仔细了解一数字签名的整个流程

公私钥是由消息的接收方所准备





才能进

但如果我们将公钥加密的操作反向进行会怎么样呢?私用于,而

用于解密,在这种情况下假设只有

私钥

的公钥去

S 对密文

B 。

B ,接收方

利用只有自己才拥的私钥

因为如此,任何人都可以使用公钥进行加密但只有 行解密

有消息要发送给

P 传递给

B 。 。

可以向不同的组织

值的密钥是两方共享,而当生成数

我们在这里简单地回顾一下公钥加密的过程:首先如果 B

生成的签名,所以

发送的带有原消息和签名数据被接收方到时,就可以保证一定是

消息的接收方



要发送一段消息“

注意,由消息的发送者准备公私钥是与 。

加密

首先

A

将自己的公钥发送给

A

如果换一

而不会是其他个体



需要准备发送的数据及一对公

不同的地方之一

。 B ,然后

A

利用自己的私钥对

在公钥

加密中,



9 章

BC

CSP

消息进行签名,结果就被用来当作数字

加密服务提供者的设计与实现

A

用 A

的公钥对数

字签名进行验证

程就结束了





将消息和数字签名



同发送给

219

B,

B



如果验证的消息与收到一致,那么整个交换过





A

的私钥生成数字签名,可以用

A

成,所以我们就可确认

A

由于

A

的公钥进行验证,但是该签名却只能由

A

的签名不能有除了

才是消息的发送者并且没有被别人篡改过

A



以外的组织(例如

B )生成,因为

同时也实现了不可否认性这一功能



B

而且



只有公钥,所以数字签名

然而,和公钥加密类似解与签名验证都

需要花费很多时间,所以我们不直接对消息进行签名

。 们



为了减少计算所需要的时间,我

首先生成与消息对应的哈希值,接着晗进行签名操作再得到数字将原 消息和利用哈希值得到的数字签名发送给

B 。

同样,

B

利用收到的消息计算晗希值,并

公钥对收到的数字签名进行解密操作得另一个哈希值



如果这两个哈希值相同,那么说

明整个过程没有出错,利用数字签名进行交换的也算结束了

。 数字签名技术提供了

authenico



falsicton

detcion



repudiaton

能,但是它本身依旧存在着一个问题消息和数字签名的接收方

B

prevntio

是消息和数字签名的发送方



但是

事实

上,很可能存在恶意第





会元条件地认为



A x

伪装成

A





进行数据交

换,存在这个问题的根本原因就于数字签名和公钥加密都使用了一私 我们并不能确定这个公钥到底属于谁



使用的公钥很可能并不来自

A

字证书”这



技术来解决

因为公钥中并没有包含它的归属方信息,所以

B

,而是来自恶意第

三方

X

的公钥



这个

问题我们可以用

“数



数字证书

9.1.9 公钥加密体系和数字签名存在的共同问题就是无法确定归属者



需要将自己的公钥发送给

B 钥替换为自己的公



解决方案



,恶意第三方

X

可以在不让

A



B

所以,当

A

发现的情况下将

A

的公

数字证书就是为了解决“无法确定公钥归属者”这一问题而出现的

。 下面我们来了解 PA



一 SA

求,让

CA



下数字证书是如何工作的

并且决定将

。 PA

发送给组织

B 。

颁布一个证明公钥

PA

属于组织

组织 A

A

假设组织

A

需要向

的证书

CA( 。

CA

CA

CA CA

提供自己的个人信息,例如公钥



利用自己私钥对组织



CA

PA A

A

提供的消息一起生成 从现在开始,

CA

同样,每个

生成的数字证书



提供的

A



就不向

自身也拥 。

CA

就是用于管理

,但是最好还相信由政府或者大

CA

、电子邮箱等

呢?

)发起请



CA

有一对公私

钥,组织

收到由组织

A

A

提供的信息时,

数据进行签名,得到字然后将生成的

个文件并发送给

A ,这个文件就变成了 B

在那之后,

certification authoriy

那什么是

数字证书的一个组织,般来说任何人都可以称为

型公司这些信用度高的组织担任

拥有一对公私钥,分别记为

直接发送自己的公钥

B

PA

A



CA

索取它的公钥,并利用

的数字证书

了,取而代之的



CA

A

的公钥验证



B

发送来自

A

发送的



•!•

220

数字证

Hyperledge r Fabric

书的

真实性



源代码分析与深入解读

数字证书中的签名仅可以用产生该

CA

换句话说,如果验证不

出问 可以提取数字证书中

A



算完成了

,那就可以说明

的公钥

该数 PA







字证书确

实是

过这个程,组织

由该

A

的公钥来验证,

CA

向组织

颁布的,然后就

B

传输公钥的过程就

。 让我们看这种公钥传递方式是否存在问题



递自己的公钥,但是

B 在认证

机构 注册公钥

X



A

的证书



X

什么

?在这

种情况下,

只能创建使用

X

通过使用数



X

A

的恶



A

证书系统,可以验公钥的所有者

法确认是谁



建了公钥

级的

X



试图传

X

冒充

A

们说 PC

X

B

收到

)是否真的由认证

伪装为认证机构创建的

。 实际上,认证

机构的



)也作为数字证书被交付,并且“签名”认机构的一方是更高等

认证机构 签名

那么

早些时候我

收到的公钥(

,它可能是由



因此,他们无法获得属



PC



的电子邮件账户,因此



换句话说,我们在公钥加密中发现的同样问题也出这里了

个公钥(

意第

。 B

建的?因为没有

无法访问

的电子邮件地址证书

了证书颁发机构的公钥,但是现在存一个问题:

机构创



。 生





没有理由相信一个作为数字证书发送的公钥

时会发

无法获得发行证书

假设一个



认证机构形成了一个树状结,高级权威为较低别的

创建

数字

。 下面

来了解这个

认证机构的树状结是如何运作

任的认证机构

Y 因

此,

G

公司



,即使新公司

G

需要 拥有

Y

分履行认证机构的职责



想要作为认证机构开始服务,它在社会上也是没有信



公司颁发的数字证书



在这之后,

G

通过这样做,大组织可以确保

比方说,我们有一个社会广泛信

小型

当然,

Y

公司会检查以确保

G

公司就可以宣称自己是一家赢得

Y

机构自己证明

其自身的有效性



。 ( rot

CA

那么,

),这



另外,如果根证书颁发机构本身不是一个值得信赖的组织

那么它颁发的证书也不会被使用

。 组织,如大公司和政府机构

公司信任的 。

机构 因此,现实中相当一部分

CA

是已经具有社会公信力的



BCSP

9.2

概要

BCCSP ( BlockChain Cryptographic Service Provide 模

块主 要提

供了区块链中

需要使

)即区块链加密服务提供者,这个

块,是保证联盟链安全的基石

用的密码学标准和算法,是整个项目中非常

重要

的一个模



9.2.1 核

BCSP

心代码的前提下

BCSP

简介 在设计的时候

考虑了一

, 可以提供不同密码学方法的实现

个非常重要的特点,就是可插拔性即在不改变

Fabric





公司能够充

组织拥有信任度,从而形成的树状结构

谁在认证机构树的顶部?最高级别书颁发被称为根



与实 加密服务提供者的设计

CSP BC

9 章 第



21 令

type BCCSP interface { KeyGen(opts KeyGenOpts) (k Key, err error) KeyDeriv(k Key, opts KeyDerivOpts) (dk Key, err error) (k Key, err error) ortOps) 呻 Keyimport(raw interface{} , opts KeyI (k Key, err error)

[]byte)

GetKey(ski

,巳 Hash(msg []byte, opts HashOpts ) (hash []byte (h hash.Hash, err error)

error) r

GetHash(opts HashOpts)

Sign(k Key, digest []byte, opts SignerOpts) (signature []byte, err error) SignerOpts) (va lid bool , err error) pts Verify(k Key, signature, digest []byte ,。 Encrypt(k Key, plaintext []byte , opts Enc

(ciphertext []byte, err error) ipterOs)



(plaintext []byte, err error)

Decrypt(k Key, ciphertext []byte, opts DecrypterOpts)

4 个 的接口中,可以看出这部分主要提供了

BCSP 在上述

功能模块: 基本

口对密钥生命周期的管理; 口对消息进行哈希处理; 口对哈希生成的摘要进行签名和验证; 。 对消息进行加密(称或非) 0



签名方法或者 加密

和 ECDSA

系列的哈希算法、 SHA

以看到整个模块大致实现了

等 系列的对称加密算法及相应中不同安全级别 const

( H

P&口&

户』户』”

ECDSA = ” ECDSA ” AAD 23A 64R 58 nunuph ssc 23e rOAAa 58R SSS AAA CCC PPR DDD EEE == EE ”口“

CU

nd

>

RSA =” RSA" RSA1024 =” RSA1024 " RSA2048 =” RSA2048 ” RSA3072 =” RSA3072 ” RSA4096 =” RSA4096 ” AES =” AES" AES128 =” AES128 ” AES192 =” AES192 ” AES256 = "AES 256 ” HMAC = ” HMAC ”

-

mD

们 中我

opts.g 在

。 项目中的可插拔性

fabric hyperldg

了整个模块在

模块中提供了很多可定制的选项,这一点也体现 BCSP

在整个 。

一种 个通用的方法群,它并没有告诉我们具体应该使哪

一 仅提供了

BCSP 因为接口



RSA

系列的签名算法

在常数定义部分可 、

AE

S

•!•

222

Hype



dger

Fabric

源代码分析与深入解读

HMACTruncated256 =” HMAC TRUNCATED 256 ' ’ SHA =” SHA ” SHA2 =” SHA2 ” SHA3 =” SHA3 ” SHA256 =” SHA256 ” SHA384 =” SHA384 ” SHA3 256 =” SHA3 256 ” SHA3 384 = ” SHA3 384 ” X509Certificate =” X509Certificate ”

在对密钥的抽象和管理方面,不论是称还非 Key

BCSP

这个接口来表示

都统一使用

。 type eyK 工 nterf Bytes() SKI () S 沪

ace { ([]byte, error)

[] byte

nmetric

() bool Private() bool PublicKey()

(Key,

对于称密钥来说,

error)

Symetric

()方法返回

false ;而对于非称密钥来说,

Symetric

私钥对应的公值

true, ()方法则返回

PublicKey

()方法返回一个 false,

PublicK

nil ey

值和

()方法返回与该



so

er

{



n

R

-工

CLa c e fMO yd ol mr e ke ea eo ry Rb rl

←」

•L

GetKey(ski

[]byte)

StoreKey(k Key)

除了对使用密钥进行

(k Key, err error)

(err error)



系列的加密操作以外,钥本身存储机制也同样非常重要

因为机器本身存在岩的可能性



如果所有密钥仅存在内中,一旦机器出现问题将

出现不可逆转的损失,所以一个靠密钥存储机制非常有必要

。 KeyStor

代表了密钥的存储系统,在非只读情况下它支持两个简单操作即

储密钥和通过

ski

取回密钥



当在只读限制条件下,

StoreKy

方法将返回

ero



因为在联盟链中大量地使用了签名算法来保证信息的真实性,所以深入解一下 算法尤其是

ECDSA

在理解

ECDSA 。



陷阱函数

9.2.2 特性

的原理对解整个签名过程还是非常有增益

如果私钥可以轻易被别人获取或者破解,那利用算法得到的签名将毫无意义

之前,首先要理解签名的真实性来源与用于私钥无法被伪造 。

所以,



9 章

BC CSP

加密服务提供者的设计

与实现

私钥无法被破解(即的难度非常大)是整个密码学前提条件

·:

223

。 利用

ECDSA

进行签名或验证需要有两样东西:私钥和公



单地用陷阱函数来描述

。 “点乘法”得到

R=k

预先知道了

假设私钥是一个随机数

·P 。

k , p 即使你知道

R



私钥和公的关系可以简

为椭圆曲线上已知的一个点,则通过

P ,你也无法通过这两个点来获取

k 值;而假如

k 值,则我们可以非常轻松地得到

R





总结一下,陷阱函数的精髓就是正向演算非常简单而逆推导难度大这 ECDSA

算法安全性的基础



为什么要使用

9.2.3

ECDSA

举一个非常简单的例子,比如你将



别人下载到的文件是



份文件传到互联网上供他人下载,但你需要保证

实的,所以你根据文件计算出相应哈希值和放在一起望

别人同样计算哈希值与他们下载的文件相对比

。 方可以篡改你上传的文件,那他们

一 不可靠

。 文件(

现在我们需要的是





但这存在一个比较严重的问题

: 如果第三

定也有办法篡改相应的哈希值,所以方并

种别人无法伪造的算,即我们使用他获取私钥对

般来说是文件的哈希值)进行签名



签名值和文件一起放在网站上,因为第



法伪造出与我们的私钥,所以他也无签名



应的公钥对签名进行验证,而确定文件真实性

方无

下载者可以利用与我们私钥对



生成签名

9.2.4 这里简单介绍



签名的长度为 合

40

下如何生成签名

( R , S )共同组成了



字节,分别由两个

20 ECDSA

签名

字节的值组成,分别是

R 20

P ,其中

示,点

P





x 值就是我们需要的

S ,它

们两个的组



那又如何来生成这两个值呢?首先,需要一 曲线上的“点乘法”获得



P=kG R ,这里

。 x 和

由于

P

字节的随机值

k ,然后利用椭圆

y 都是

点可以由

( x,

20

y ) 这样的坐标表示法

字节,得到了

R

之后,接



来我





S 。 为了计算

S 的值,你需要对原始消息进行

SHAl

哈希

值,你可以将它理解为非常大的一个整数这里记



z 。 公式





哈希的结果是一个

20

得到了

字节的

z 之后,我们可以通过下

S:

S=k/\-1 (z+dA *R) mod p 值得注意的是,这里护 于

1 。

- 1 的意思是



再来重复一遍这里用到的几个关键信息

模逆元的性质就是(

k/\

: k 是我

z 是我们消息的哈希值, 的指定起点坐标

k 的模逆元



dA

是我们的私钥,

R



k*G



们随机

生成的用于计算

x 坐标

-1

*k) modp



R

G

是我们用到的椭圆



等 一个数,

曲线

•!•

224

Hyp erledger F a bri

c 源代码分析与深入解读

验证签名

9.2.5 在得到了签名之后,为验证它的正确性你只

需要

椭圆曲线的参数),便可利用下面公式来计算

用于签名的私钥所对应公(及

P: P=SI\

如果

P

点的

x 坐标的值等于

R

签名的数学证明过程

*R*Qa

一 lzG+S/\-1

值,则说明签名是有效的否无



下面是验证

。 因为

P=S



/\ -l

zG+S

Qa=dA*G

/\ -1

*R*Qa

,所以我们有下公式:

P=S A-l zG+S /\-1 *R*dAG=S /\-1 (z+dA *R)*G 而

P



x 坐标应该

等于

R ,而

R



k*G



x 坐标



这意味着



kG=S /\- 1(z+dA *R)G 当两边同时移去

G

我们有



k=S /\ _ l (z+dA 进一

步变



得到

呗)



S=k/\- 1(z+dA *R) 这就是我们之前用于生成

S

确的

的公式,两者相符说明上面用于验证签名是正



BCCSP j 原码剖析

9.3 本节将讲解

BC

BCSP

CSP

区块链加密服务这一模的设计与实现



服务涉及的算法

D RSA

: : 一

D AES

种非对称的加密算法,用于



: 一种块加密

D ECDSA

算 : 一



Has



HMAC

h :

法,用于加密成块的大



种椭圆曲线签名,用于

哈希

。 。



几种

族簇,如

SHA2

56



种,可参看第

D PKCS#l l : 一套

12

9.3.1 BCSP

几种族簇,如

AES128 EC

SHA3

256

章中对证书的解释



删除







以上算法可用来进行建立、

DSAP256



DSAP256



BCSP

ECDSAP384

等几种类型

. go

中开始部分定义,如

、 ECDSAP384

等 等

ECDSA



服务结构 Fabric

项目提供各种



密技术、签名,

MS

P 服务模块中就使用到了



、 AES192

。 fabric/spot



RSA2048



等操作管理

用到的这些技术常量名称在/ EC



标准安全接口,可与硬件相关

读取、写人修改 支持







: 证书的

Fabric

数据

R SA1024

有几种族簇,如 、

:与密匙相关的哈希运算消息认证码

D x509

有几种族簇,如

。 。

。 选项实现

的 模块提供窗口

他 模块提供了窗口函数(就是给其

服务的 BCSP

1.

函数





中的接口和选项 BCSP

9.3.2

量 所定义的全局变

. go

/ factory factory

实例存储在 BCSP

生的

接口 代码如下:

接口相关

中定义

/ bcsp.go

/ bcsp fabric

type BCCSP i nterface { 生 key /根据

key 来生成一个

opts 成选项

” 证书一级认

( 与“ key

要适合原始的 选项

, opts

有关的选项 key

/与

对应



KeyGen(opts KeyGenOpts) (k Key , err error) 一个 重新获取

k 中

, 从 opts

y 获取选项 ke

根据 /

KeyDeriv(k Key, opts KeyDerivOpts) 据 /根

导入 key

opts 选项



从一



原始的数据中导 key

key (dk

Key, err error) 入





key

Keyimport(raw interface{}, opts KeyimportOpts) (k Key , err error) /

根据

SKI

返回

与该接口实例有联系的

key

数一 这些函

函数,

InitFacores 管理自己服务的功能模块,供外界调用如后文所讲





Security Modules Hardwe



II

服务实现,这里有两种工厂为其 BCSP

相对应的两种 。

安全模块 即硬件

htps:

HSM 而

这个库为基础,

工l kcs

/p

/ miekg github.com

是硬件基 pcksl

, 中



这个硬件基础的实现以 。

是软件基础的加密服务实现 SW

现, 密服务实

。 SW

l 和 pkcsl

服务有两种实现: BCSP

从以上可看出,

/在

服务可以使用到的各种技术 BCSP

表示该目录下的各种值,

: X Xopts.g

。 不存储

则 果是暂时性的,

:如 该接口的实现对象中

存储在 的,则

不是暂时 key

的管理存储接口,如果生成

k ey 定义了



. go tore

keys

。 等

i v Opts KeyDr

、 KeyGnOpts



K 町

到的选项接口,如 接口所使用

BCSP 、众多

口 接

Key 、

BCSP :定义了

D bcsp.go 口

。 务工具函数

服 BCSP

l s: uti

implementation

) 。

of the BCSP 口

-based

(the so 位 ware BCSP

一软件基础的 二

务实现之 服

0 sw: BCSP

般统

( the hsm-based BCCSP

) 。 implentao



BCSP 基础的

HSM →-

服务实现之一

11 : BCSP pkcs



。 务工厂

服 BCSP

factory: 口

使用

签名 向外界提供

签名接口专用于 的

该目录 。

服务实现中 SP

M

1 2 章中

。 对象



。 服务

接口,可参看第

s igndety 身份



225

,目录结构如下: 中

Signer 的

标准库

c rypto

现的 是 实

带“专用签名笔”

缩写,

/ bcsp

BCSP :

signer 口

础的加

/ fa bric

:模拟代码文件夹,可以参看帮助理解 mocks



码集中在 服务的代

BCSP 。

BCSP

9 章

•! 加密服务提供者的设计与实现

BCSP



) 。

所有产

•!•

226

H y p erledger Fab ric i 原代码分析 ,

GetKey(ski

与 深入解读

[] byte)

/根据哈希选项

opt

(k Key, err error)

s 对一个消息

msg

进行哈希计算,如果

op

Hash(msg []byte, opts HashOpts) /根据哈希选项

opts

获取

has.

Hash

GetHash(opts HashOpts) / * 根据签名者选项

opts

ts

为空



则使用默认选项

(hash []byte, err error)

实例,如果

opts

为空



则使用默认选项

(h hash.Hash, err error)

, 使用

k 对

digest

进行签名,注意如果需要对一个特别大的消息

has

/进行签名,调用者则负责对该特别大的消息、哈希计算后将其作为

diges

Sign(k Key, d i gest []byte, opts SignerOpts ) /根据鉴定者选项

opts



通过对比

k 和

digest



p ts





使用

k 加密

(signature []byte, err error)

据解密者选项

opts



使用

k 对

ciphe rtex

[] byte, err error)

工 phertx

Dec



(plaintext []byte, err error) ipterOs)

选项

BCSP 套

(c

进行解密

Decrypt(k Key, ciphertext []byte, opts

2.

(val id bool, err error)

p laintex

(k Key, plaintext []byte, opts EncrypterOpts ) icrypt

/根

文件夹中任何

带“

选项,我们在讲

MSP

opt

”字眼的文件



服务的时候就见识过



都是和选项有关的源码

根据

。 一

关于对象

的配

个选项的不同配置,对象主体可以得

到不同的数据或进行操作,这也是一种比较值得学习语言上组织技巧尤其 项



BCSP

这种涉及的技术比较多,而每个对象自身又分为好类情况下

Hash



Opts



/在

key

导人选项

Key

ImportOs

fabric/sp.go /

在此以哈希选

作为例子进行说明:

中定义

哈希选项接口

type HashOpts interface { Algorithm() string / 取哈希算法字符串标识获 /在

fa

bric

/bcs

p /has

/哈希选项实现之一



hopts.g







HA256

”唱阻

3

256 ”

中定义

SHA256

选项

type SHA2560pts struct {



QU



mm 飞dc

←」

←」

s s

JZ 电 {‘

--

【 b

8

之 严





L

现 ω



’’’’- ’

州附

飞’

蚓则

func (opts *SHA2560pts) Algorithm() string { return SHA256

、、,,

func

(opts *SHA3840pts) Algorithm() string { return SHA384

/在

fabric /key t 归

/在

* /

鉴定签名

Verify(k Key, signature, digest []byte, opts SignerOpts) /根据加密者选项。



t 传入

/bcs

p /bcsp.go

中定义

e

导入选项接口

KeyimportOpts interface { Algorithm() string /返回 key 导 Ephemeral () bool /:如果生成的 fabr

i c/bsp

/。

pts.go

中 定义

入算法字符串标识

key

是短暂的(

eph m

ral

),返回

true

,否则返回

false



/key



入选项接口实现之





ECDSA

9 章

公匙的



BCSP

加密服务提供者的设计与实现

·:

227

入选项

type ECDSAPKIXPublicKeyimportOpts struct { Temporary bool func

(opts *ECDSAPKIXubl return ECDSA

func

(opts *ECDSAPKIXPublicKeyimportOpts) Ephemeral() bool { return opts.Temporary

/key

工 cKeyimportOs)

导入选项接口实现







Algorithm()

ECDSA



匙的

string {

导入选项

type ECDSAPrivateKeyimportOpts struct { Temporary bool func

(opts *ECDSAPrivateKeyimportOpts) Algorithm () return ECDSA

func

string {

(opts *ECDSAPrivateKeyimportOpts) Ephemeral () b o ol { return opts.Temporary

/比

较特殊的



/由于

SignerOpts ,

SW

BCSP







因此使用到选项时多赋值为

口 il

Soft

Ware (SW

厂的默认选项

源码中未实现

/ fabric

和核心配置文档中关于 has



BCSP

)实现形式是默认的,这点仅从

DefaultOps

主要使用的包是标准库 eliptc

,

实现方式

9.3.3 go

比如签名选项接口

使用的是标准库

x509

等)



BCSP

/ bcsp/fatory

crypto

的配

(包括其中的各种,如

aes

/ opts.

置 、

rsa

就可以 、

看 ecdsa



出来 sha256

。 、



/fabricspw

目录结构:

D imp!. go : BCSP D interals.go





! 。

函数、加密者

/ 解密者实现 、

类型的签名者

公匙

、 公匙

巳 y.go

类型的

:巳 rsa

eds

Key

a 类型的 Key

短暂的,则说明这些

key k 巳 y 就消失了

。 。

接口实现



接口实现 类型的



/ 私匙鉴定者实现

Key

类型的



/ 私匙鉴定者实现

接口实现

D dummyks.go : dumy

D fileks.go : file



key

D aeskey.go: aes rsakey.go:

实现的配置定义

类型的签名者

D rsa.go : rsa ecdsak

SW

类型的生成

D ecdsa.go: ecdsa



imp



D aes.go: aes



实现的

:签名者、鉴定加密解接口义

D conf.go: BCSP



SW

。 KeyStor

接口实现

dumy

KeyS tore

不会保存到文件中,而是内系统



当生成的

key



关闭,这

类型的



KeyStor

接口实现

fileBasdKyStor



若生成的

key

不是短



•!•

228

Hyperledger Fabri c j 原代码分析与深入解读

暂的,则说明这些 key

会 消失

在导人时,会存储文件中即便系统关闭这些 key



BCSP 接口实现:

/ 在

/ fabr

工 c/bspw /工

mpl.go 中定义

//SW CSPB 的实例结构体 type impl struct { conf *config ks bccsp.KeyStore encryptors map[reflect.Type]Encryptor decryptors map[reflect.Type]Decryptor signers map[reflect . Type]Signer verif



工 ers map[reflct.Ty]Vi

/ BCSP

/

实例的配置

key

I

存储系统对象 ,

I

Key 对象

; 解密者映射

/签名者映射 ,

工 er

存储和获取

加密者映射

/鉴定者映射

Key ,

实现的类型作为映射键

Key 实现的类型作为映射键

专用生成函数

func New ( /

)

(be esp BCCSP, error)

{ . .. }

接口实现

func

(esp 吐

rnpl)

KeyGen(opts bccsp.KeyGenOpts)

粗线条上看,由于 imp

switch-ae 如

也不

(k bccsp.Key,

... ) { ... }

I 对象和各种操作选项的存在,绝大部分接口实现都是以

为主干,根据选项的类型或配置分情况完成功能且每个支操作都似 Key Gen 。 接下来 一

D KeyGn

介绍:

:根据

ECDSA

key 使用库巳

数(在

生成的选项不同, 三

cdsa

aes.go

的 GenratKy

种系列的

函数,

key

中实现,包装了

AES

rand.Re),

,即

RSA



实现对象,如

Xkey.go

ecdsaPrivtK 中)

系列的 key



使用库 rsa

D KeyDriv :根据

opts

k

代表 Public



,即

Private )和

:从原始的数据 raw

ECDSA

key 为了得到 utils.Cone

key 和

,对原始数据

) 。 raw





指定的

模块是

key 。

hmacedKy 中

类型,即

最后返回打乱 。

ks

MSP

这里的重新获

处理两种 对于重新生成的

key

。 ,如果该 key

这里的原始数据,指是口 byte

不是临时性

或者含有 key

是一个空接口,也就说可以收任何形式的原 raw

utils.DERToPbcKey





BCSP



aesPrivtKy

opts

key

key

,则会存储在

中取出选项

中,最后返回

的数据(如证书里

个 和

key

key key

reRandomizKy

,如果选项中指定的不是暂时性

ks



中重新获取一个 一

key

的,则存储在

工具函数

,从



最后返回

,只支持

。 ECDSA

重新生成的两种类型 Import



町等(分别定义在同目录下

k 中的内容重新打乱再生成



函数



,也只有被调用者能决定使哪一种

获取选项

ecdsaXXXKey ( X

始数据

巳 y

,具体的细节略过

aesPrivtK

,应该是在它这个地方只认

key

取可以理解为把

Key

GenratK

key



。 函



的使用者之

key

RSA

,这是官方文档中所说的但从实现上看签名支持不止一种 key

BCSP





这里要注意的是,当前版本中用于签名

本质上无论实现多少种





AES,

GetRandomBys key

key



使用自定义的

个系列又根据具体参数的不同生成“尺寸”

不同的

ECDSA

进行转化或抽取, ,如

一 AES256 、 ECDSAPrivateK

部分使用了 utils

町等类

下的

Pu 表示

x 里

的 New



Typ reflct.

型为

aescbpk7Enry 追溯,

0 Decrypt : 解密与加类似,也是只有 。

巳 Of(k

or

使用

aes

的接口 。

库的加密流程进行

aes

配套实现,最终使用

aes

获取类 ryptos

enc encrypto

;然后直接调用 encrypto

)的加密者

。 被使用

aescbpk7Enryto

实例的加密者集合成员 BCSP

这里加密的实现过程是,首先从该

解密

町, aesPrivtK

赋值部分,只有 rs

encrypto

专用生成函 BCSP

SW 参看

。 -aescbpk7Enryto

是用来加密的) aes

只有

,因为 aes

中实现(只能是 aes.go



加密者接口 。

接口实现如上描述

中,在 interals.go

定义在同目录下的 Encrypto

Key 。

中的实现 BCSP

SW 在

Encrypto 者接口

加 接口和

Key 密涉及*

加 。

tex plain

k 加密 ,使用

opts :根据加密者选项

0 Encrypt

函数(这 PS

Verify

) 。

i vate Pr

或 blicKey

Verifier

库的 rsa

使用 Verif

rsaXKey 函数,

Verify 库的

dsa ec

使用

ecdsaXKy ;追溯,

Verify 的接口

verif ;然后直接调用

verif 鉴定者

)的 TypeOf(k

reflct. 处获取类型为

verifs 实例的鉴定者集合成员

BCSP 首先从该

这里鉴定的实现过程是, 。

接口实现)都有用到 Key

分,可知所有鉴定者(与对应的

赋值部 verifs

的 New

专用生成函数 BCSP

SW 参看



V 巳 rife/saPvtKyV

巳 yKe­ rsaPublicK

中的 rsa.go



中的

. go ecdsa

er/ ecdsaPri vateKey Verif

飞 1erif

ecdsaPubl icKey Key

鉴定者接 。

接口实现如上描述 Key

中,有两种类型的实现即 interals.go

定义在同目录下的 Verif



。 中的实现

bcsp SW

在 1erif

i 鉴定者接口飞 口和

接 Key

鉴定涉及 。

,鉴定签名 digest

k 和 ,通过对比

opts :根据鉴定者选项

Verify 口

。 函数

Sign 结构体的

PrivateKy 库

rsa 使用

rsaSigne 函数,

Sign 库的

ecdsa 用

使 ner

cdsaSig 追溯,巳

; Sign

的接口 signer

;然后直接调用 signer

)的签名者 TypeOf(k

reflct. 中获取类型为

signer 实例的签名者集合成员

BCSP 实现过程是,首先从该

。这里签名的

- rsaSigne rsaPivteKy



- ecdsaSignr ecdsaPrivtKy

,即 Signer



Key 赋值部分,可知用到了两种对应类型的

s igners 的

New 专用生成函数

SW BCSP

参看 。

rsaSigne 中的

rsa.go 和

cdsaSigner 中的巳

ecdsa.go 中,有两种类型的实现即

interals.go 定义在同目录下的

Signer 签名者接口

。 里签名(自然)使用的都是私匙

这 。

aesPrivtKy 中的

aesky.go 和

rsaPublicKey/vt 中的

rsakey.go

K 町、

BCCSP

ecdsaPubliKy/rvt­ 中的

ecdsaky.go 种实现,即

三 接口有

Key 。

中的实现

SW 在

ner Sig

接口和签名者 Key

签名涉及 。

nil 没什么用处,调者都给的是

进行签名,这里的者选项在当前版本 digest

k 对 :根据签名者选项,使用

0 Sign

。 晗希家族

SHA3 SHA2,

默认选项,支持 则使用

空, 为

o pts 果



。 的哈希值

msg 进行晗希计算,返回该

msg 个消息

一 对

opts

0 Hash :根据哈希选项

、 町

。 key

町等类型的 RSAGoPublicK

229

ECDSAGoPublicK 类型),如

raw.(*Key 语言的断

Go 部分直接用

一 ;

key 型的

•! 密服务提供者的设计与实现

;tJD BCSP

9 章 第

库解密流程进行

Encrypt;

•!•

230

Hyperledger Fabric j 原代码分析与深入解读

D GetXXX: GetX pkcsl

9.3.4

系列接口,获取实例对象中的数据具体这里不再详述



实现方式

BCSP



pkcsl

1 实现形式主要使用的库与

SW

github.com/miekg/pkcs 1

库,最好参看其文档以熟悉

BCSP

的功能,也为

BCSP



出来:



符设备联系在



U 或芯片只

需要 对于

。 (参看下文)中所调用的





l 实现

这点可以从

lo adLib

比如将来,开发出了



pkcs

11 , Fabric

函数看



1 所提供的接口,在此两个文档地址读者可以稍作了解

: htps: htp:



/ 6 的

1 eac56/indx.html#

hapter2-9

是通用的接口,所以有

一 impl.go:



:定义了 Opts



pkcsl

l.go



:以



/ E

19253 -01/819-

区域链无关,但是因为

pkcs

库中的解释相对过于简单 imp

11



!。

服务的配置和

PKCSlOpts

1 库



. go

FileKystorOp



DumyKestor

k 町

! 在巳 aes

ecdsaky.go

eds

l 功能



,实现了

imp

l 基于

pkcsl

l 的

dsa



signECDSA Key

ec

加密、解函数

a 技术下的签名函数

类型的

:实现

PrivateKy

pkcsl 服务使用到的独立内调函数

类型的生成

imp :实现

为基础,包装各种

BCSP

aes

: 实现

D aesky

Fabric

pkcsl

l 实现的

pkcsl

:实现

ecdsa.go 口

pkcsl

一些

a es.go

/ cd



内调函数,和 口

这些文档与



BCSP

选项

/ docs.ralem

/ /w.

目录结构:

bcsp

conf.g



定参考价值

/fabric/bccsp/pkcs 1 口

款在区域链上使用的,类似于现

即可对此进行支持和扩展

ibm.com/developerworks/cn/security/s pk cs / 和

和鉴定函数

接口,只实现私匙

类型的

Key

verifyECDSA

aesPrivtKy





接口,实现了公匙

ecdsaP

ublicKey



私匙

ecdsa

rsakey.go



:实现了

rsa

类型的

Key

D dummy ks.go : dumy D fileks.go: fi le 类型的 BCSP

11 ( Public-

盾之类的确认个人身份或安全交易硬件模块芯片,这些

也遵循 pkcsl



pkcs

加载了一个系统中的动态库,能就可以和驱、热插拔

连接电脑的





支持热插拔和个人安全硬件模块提供了服务 New

在网上银行所用的

7056

1 的简要操作

pkcsl

l 的实现例专用生成函数

loadLib

辙,只不过外加一个

)是一套通用的接口标准,可以说这里

Fabric

pkcsl



pkcsl

Key Cryptography Standards, PKCS 了

实现如出

Key

类型的

接口,实现了公匙

rsaP

Store

KeyStor

接口,实现

ublicKey

和私匙

rsaPivteKy

DumyKeStor

接口,实现





FileBasdKyStor



接口实现:

type impl struct { conf *conf ig ks bccsp.KeyStore ctx *pkcsll.Ctx sessions chan pkcsll .SessionHandle slot uint /

配置

/

key

存储系统对象

, pkcsl

uint /安全硬件外设连接插槽标识号

/实质是

/标准库的

存储和获取

Key

对象



上下文

会话标识符频道



默认

10

个缓存



9 章

BCSP

加密服务提供者的设计与实现

•!

231

/加载库所在路径

lib string noPrivimport bool softVerify bool /禁止导入私匙标识 /使用软件方式鉴定签名标识

} /专用生成函数 func New(opts PKCSl

keyStore bccsp.KeyStore)

工 Opts,

(bccsp.BCCSP , error)

/接口实现

func

(esp *impl) KeyGen(opts bccsp . KeyGenOpts)

BCSP



pkcsl

现的语句时,

l 实现的骨架在

SW 则用

pkcsl

话(

imp

实现是使用

l 包对

crypto

I.go

中与

(k bccsp.Key, err error)

SW



实现基本一致,只是追溯到最终

库下的各个包进行签名



数据进行了多一层的处理,使用

pkcs

SesionHadl

1

加密

、密匙

导人等;而

提供的上下文(

)之上对签名、密匙加等进行管理,这也是

两者最大的不同是

pkcsl



pkcsl 1 中与安

个面向软件,



个面向硬件

全硬件模块建立连接的

loadLib



loadLib

函数在

pkcsl

I.go



pkcsl



pkcsl

pkcsl l .Ctx

l

)在会

l .g o 文件的作用



自身又非常冗杂,因此在只讲



中定义,供专用生成函数

New

使用



为建立与安全硬件模块

的通信,进行了如下步骤:

( 1 )根据所给的系统动态库路径 用

pkcsl

lib

l .New(lib

bcsp

<一

)建立

> ctx

pkcsl

<->驱动

( 2 )对

lib

o

ctx

相当于

Fabric





ctx

. GetS lotList( true

ctx.OpenSsio

打开



个会话

c tx.Login

,把



seion

关于

返回

1

发送到

pkcs

ctx



I.go

libsofthm2 。

BCSP

Fabric

中某



。 / bcsp

/fact

ory

impl

实例成员





FindPKCSl

. so

导人民

库,它是一个模拟硬件实现的 !Lib

pkcsl, 测试函数中所涉

及 的相

csl

对象

关内容,如



实现,这里也有两种

一模

窗口函数,供其他模块调用

,用于赋值给

现阶段是没有安全硬件模块可以配合测试的,所只使用

工厂对应两种

/ fabric

labe

) 。 seion

SoftHM

模拟测试,将

swfactory.g



(会话就是通过信路径与

chan

、会话对象

,还有一点可说的就是

libsofthm2.

BCSP

l 的

slot



imp

SoftHM



seion

对应到系统动态库可参看

下的

标准开发的

)返回的列表中获取由

seion

pkcsl

Linux

l



次调用

( 4 ) 登录会话 slot

pkcs

一个,每在系统内核中都有名字和标识号)

10

安全硬件模块建立连接,可以简单地理解为 、

是按照

(这里的插槽可以简单地理解为电脑主机上供安全硬件模块入,

插口,可能不止

( 3 )尝试

ctx

的动态库),调

与安全硬件模块通信的桥梁: lib

()进行初始化 slot

USB

ctx

openCrytki

<一>安全硬件模块,只要驱动

ctx.Inialze

指定的插槽标识



1 实例

加载动态库(如

这里以

一旦

be

涉及工厂

factory

swfactory

esp

工厂:

pkcs

11 factory.g

,则说明该模块基本就是由工厂提供



为例进行讲解



/目录结构:

0 factory.go : 声明了默认 等全局变量和这些的获取函数

BCSP

0 nopkcs 11.go/pkcs 1.go

实例

:定义了两种版本的

Ge

defaultBCSP,

BCSP

tX 工厂选项



定义

BCSP

实例存储映射

FactoryOps

工厂接口

bcspMa

,即初始化工厂函数



23



H ype

InitF

rl edg

r Fabric

a ctorie

源代码分析与深入解读

s 和获取指定

B CS

P 实例的函数

GetBCSPFromOps



版本,可根据相应条件编译指定使用哪种(

译时 项)



两种版本的差异集中在是否使用

pkcs

0 opts.g 口



定义了默认的工厂选项



DefaultOps

pkcslfatory.g:

pkcs 1l 类型

0 swfactory.go : SW 选项

1

类型的





no

pkcsl

是默认

1 或!

nopkcsl

l 选





B CSP

BCSP



nopkcsl

工厂实现

PKCS

工厂实现

1lFactor

SWFactory



y 。

还定义了

SW

版本的

BCSP



swfactory

接口和

/;在

实现

factory.g



中定义

/接口

type B CSPFactory

工 nterf

/返回工厂的名

ace {



Name() str ing /返回符合工厂选项

o pt

s 的

BCSP

实例

Get(opts *FactoryOpts) /在

swfactory

. go

(bccs p. BCCSP , error)

中定义

type SWFactory struct{} func (f *SWFactory) Name( ) string { return Sof t wareBasedFactoryName func

Get(conf

(f *SWFactory)

工 g

实现的代码本身比较简单, opts

Ge 的

bcsp

实例的

在每





chain

Name

code



的垫片

shim

例子中,如

核心代码 shim

/ fabric

/ core







shim



DefaultOps opts.g

)来初始化



chain

code

New

垫片

shim



函数就是用来启动一个 。

c hain 在

Star

个默认的



函数中,就调用了

swfactory



工厂,在此可以知道这里使用的默认

Star

函数

形成的通信息,过



shim code

,其被定义在 e r

BCSP

中的

chainode

中,该垫片所承的是与各个节点通 c hain

Star

来生成符合

_ cli/examples/chaincode/go/chaincode_

code / shim

{ ... }



责从各个节点收集信息,汇总并返回给

一 )就是使用的

c hain

/ chainode

专用生成函数的

常量

/ e2 了

服务 负

shim/ chaincode . go (参看

中在

SW

/ exampls

,都使用

ChaincodeSuprt 节点,然后

一个

/ fabric



信的任务,也即

SW 返回

. go

(bccsp .BCCSP, e rror)

t 最终是调用的

是直接

example02/chaincode_ exampl02



*FactoryOpts)

code



/ fabric



完成

chain

/ core

发到各个 code





/ chaincode/

:= factory.InitFactories( &factory. 工厂

选项

阜旱

响PP



、J

dE

,

HHH .·



····

..... ....... ·-·. .... .... ... ... G UTUh’ r U u -

Fabric CA

Fabric CA 功能



Hyperldg

r Fabric

架构设计与讲解

提供证书

机构的功能



具体来说,

Fa

bric

CA

提供以下



( 1 )身份注册,或者将连接到

LDAP

( 2 )颁发登录证书(

ECe

( 3 )证书续期与撤销 包

对贡献



) 。

含一个服务端组件和客户,稍后会进行介绍

Fab



ri c CA

感兴趣的开发者,可以参考

Fabric

Fabric CA

10.1 10-



Fa

bric

CA

Fabric

Fabric CA



ca

/ swag

/ swager

CA reposity



用户指南

所示说明

有两种方式可与

这个文档

为用户注册



Fabric CA



rts



CA

服务端如何在

交互都是通过

Hyperldg

RES

服务端交互:通过

Fa

T

AP

巳 r- fabric-ca.j son



ls

来实现的



你可以通过

Fabr

i c CA

R EST

APis

htp:

bri

c 架构中发挥作用



客户端或

Fa



swager

bric

SDK



所有与

说明文档见

fa bric­

/ edi tor2 .swagger. i 。 在线编辑器来查看



Fabric CA

客户端或



端,这一部分可以

参看图

S DK

HA

代理节点为

Fabr

所有的

i c CA

Fabric

配置了 一

LDAP 个服务端可能包

CA

,那

10-

可能

会连接

右上部分



集群作负载



到 均衡

Fabric

一 个

个 HA

Fabric

CA

服务

代理节点,这个



么用户信息将会保存在

L DAP 证书

中的某 一

。 CA

集群

图中客户端连接的是

务端共享同一个数据库 含多个

CA



每一

数据库用来保存户和证书的信息





中,而不是数据库 CA

。 证书都

是一个根

CA



书或

者一

个中间

如果

MW



.>

234

CA

证书





Hyperledger Fabric

CA

证书



每一个中

源代码分析与深入解读



CA

证书都有

一个根

CA

证书或者其

他的一个中间

CA

证书作为其



Cluster of Fabric CA Server Fabric CA Root Server



I

Fabric CA

0-1

的作用示意图

入门 下面来体验

Fabric

1.

CA



前置条件

请检查系统是否符合条件

: 口安装了

Go

D GOPATH D libto

1.9

+ 。



环境变量设置正确

。 libtdh-ev

以下命令用于在

两个已包安装好

Ubunt

系统安装

sudo apt install 以下命令用于在



l工

libto: 1 ibltdl -dev

btol

MacOSX

系统安装

libto:

brew install libtool 若要了解更多有关

libto

的信息,可参考

manul/ht_odeUsig

若要了解更多有关

libtdhr -l ibltd.hm

-de

htps:/

v 的信息,可参考 。

/w

htps:/w.gnu

. gnu.or/sftwae

libt

. org/sftwaelib

ol/



•! 计与讲解

架构设

Fabric CA

章 10



235

2 . 安装

fab 果你已经下载了

:如

, ric-a

分 master

『命令时确保当前位于 get

go 在运行上述’

: 会看到如下报错

则你将 支,否



- client

- ca fabric

u github . com/hyperledger/fabric-ca/cmd/ ...

go get

注意



『 serv fabric-

路径下同时安装 $GOPATH/bin

以下命令会在

ca ; git pull --ff-only 工 C . com/hyperldgfab /srciub for the current branch. 工 on There is no tracking informat Please specify which branch you want to merge with . git pull If you wish to set tracking information for this branch you can do so with: branch - set upstream-to=/ tlsdoc

g 工 t

package github.com/hyperledger/fabric-ca/cmd/fabric-ca-client: exit status 1

原生启动服务器 3.

fabric-sev: 默认配置启动

fabric-ca server start -b admin:adminpw

ID 来提供启动管理员的登录

b 选项用 :-

其中

会自动在本地目录创建,这个配置文件可以

- config.yaml

fa bric-asev 默认配置文件

。 的

是必需

- b 选项

enabled ”设置,则

“ ldap 没有启用

L DAP 码;如果

和密

。 自定义

启动服务器 Docker

通过 4.

。 完成准备工作后,使用命令行来启动服务器

1 ) Docker Hub

docker



- compse.yl docker



ym

” compse.

/ docker

/ fabric-

/ hyperldg

/ github.com

/ src $GOPATH

进入



的标 签 合

版本相符

- ca fabric

/ ,找到与你想下载的架构和

/ tags

/ fabric-

/ r / hyperldg htps:/ub.dockerm

访问



l 文件如下所示

fabric-ca server: image : hyperledger/fabric-ca : x86 64-1.0.0-beta 工 c-aserv container_ name : fabr ports: - ” 7054:7054 ” 口 ment:

e 口 viro “

FABRIC

_CAHOME=/etc

/hyperldgfab

工c

路径并在编辑器中打 beta

一行进修改。 image

根据之前找到的标签在

/ serv

- ca-serv





x86

架构的

.

236 令

H yp e rledger Fabri c j 原代码分析与深入解读

volumes . -

”.

/ fabric-sev

工 C ca server ” b admin:adminpw ’

: /etchyprldgfab

command : sh -c ’ fabric ca server start



docker-mps.yl

文件的根目录下打开一个终端并且运行如命令



# docker-compose up -d

如果指定的 动

fabric-

fabr

ic-a



镜像之前并不存在,这个命

令将

务端的一个实例

Docker

你可以通过

docker

进行下载



这个命



还会



镜像

” compse

cd ♀ GOPATH/srcgithub make docker cd docker/server docker compose up

构建和启动服务器,

Fabric





工 c-a

d

CA

这一部分提供

具体如下所

. com/hyperldgfab

hyperledger/ fabric ” ca docker

体验





2 )构建你自己的

5.



镜像包含

fa

bric-asev



fabric

- ca

- client



命令行

fa bric-a

” serv

在接下来的内容中提供

巳 r 和

fabric-lent

命令行的使用说明



其他使用信息会

。 fabric ca-server : ”

Hyperledger Fabr

工 C

Cert

Authority Server

工 ficate

Usage :

Ava

fabric-ca-server [command] 工 labe

r





ev

r

L

V



-

D 、

如L

fr

工尸

L

al ←」

start version

e

Commands : hbe ta ISP - trn zea abcs avsre erfCA csv err eo as er rae lhF ntr rs -ee 4 、

工t

叮 A占←

工 n

Flags : --address string Listening address of fabr 工 C -ca-server (default ” 0 . 0 0 . 。 ” ) b, boot stri 口g The user:pass for bootstrap admin which is required to build default config file --ca . cert f 工 le str 工 ng PEM-encoded CA certificate file (default "ca-cert.pem ”) -ca . cha 工 nfile string PEM encoded CA chain f 工 le (default ” ca-chain . pem ” ) ca . keyf 工 le string PEM-encoded CA key file 口, -ca . name str 工口 g Certificate Author 工 ty name - -cacount int Number of non default CA instances - cafiles stringSlice A list of comma separated CA



10



Fabric CA

架构设计与讲解

•!

237

conf 工 gurat 工 on files cfg.affiliations allowremove Enables removal of affiliations dynamically 句“ c fg . identities . allowremove Enables removal of identities dynamically crl . exp 工 ry duration Expiration for the CRL generated by the ge 口 crl request (default 24h0m0s ) -crlsizem 工 t 工 nt S 工 ze 1 工 m 工 t of an acceptable CRL in bytes (default 512000) --csr . cn string The common name field of the certificate signing request to a parent fabric-ca-server --csr hosts str 工 ngSlice A list of space-separated host names 工 n a certificate s 工 gni request to a parent fabric-ca-server -csr serialnumber string The ser 工 al number 工 n a certificate signing request to a pare 口 t fabric-ca server --db datasource string Data source whic 工s database specific (defaul t ” fabric-ca-server . db " ) --db . tls . certfiles stringSlice A list of comma-separated PEM encoded trusted certificate files (e . g . rootl . pem , root2 pem) --db . tls . client . certfile str 工 ng PEM-encoded cert 工 ficate file when mutual authenic 工 s enabled --db . tls . client . keyfile string PEM-encoded key file when mutual authentication is enabled db. type string τ'ype of database ; one of : sqlite3, postgres, mysql (default " sqlite3 ” ) -d, --debug Enable debug level log 工 ng -H, --home str 工 ng Server ’ s home directory (default " /etc/hyperledger/fabric-ca ” ) --intermediate . enrollment . label string Label to use in HSM operations 工 ntermdia enrollment . profile string Name of the signing profile to use in isu 工 ng the certificate --intermediate.parentserver . caname string Name of the CA to connect to on fabric-ca-server -u, --intermediate . parentserver url str 工 ng URL of the parent fabr 工 cca-server (e . g . http : // : @ : @< host 为 true

port 。

务端验证身

int

bric CA

secrt

:// ::
SHOW GLOBAL VARIABLES LIKE



Variable name

Value

have_openssl

YES SL

器用户

( 3 )服务器端 。

为此,请登录到

置完 配

MySQL

确认表 SL

10-3

name

「 iable Va

Value

have ssl 一

成后,下-步是创建

YES SL

个有权通过

服务 MySQL

访问的

服务器,然后输入:

‘ sluer mysql> GRANT ALL PRIVILEGES ON . TO REQUIRE SSL; mysql > FLUSH PRIVI LEGES;

( 4 )如果想给出用户访问服务器的特定

sl ’ ;

。 所示的各项

10-3 该看到表

此时应

s 一在

‘ have

IP

地址,应将



由 ’

IDENT IFED

草 ’

’%

’ 更改为特定的

‘ password ’

BY IP

地址



•!•

246

H y pe

MySQL



dger

Fabri

c 源代码分析

与深入解读

服务器需要客户端证书

安全连接的选项与服务器端使用类似。 D sl-ca

:验证书颁发机构(

CA

使用的相同证书

)证书



该选项(如果使用)必须指定与服务器



D sl

- cert

:验证

D sl-key

MySQL

:验证

服务器的证书

MySQL



服务器的私钥



假设你要使用无特殊加密求的账户进行连接,或者包含

REQUI

GRANT i 吾 句创 项来启动

建该

MySQL

CA

账户



服务器

服务器



作为推荐的一组安全连接选项,至少使用 。

s l- cert 然后在服务器配置文件中设

db

若要指定客户端证书,使用

requi

定适当的客户端密钥和证书文件,否

则 db.

配置

L D A



Fabric CA



LDAP

q

X5

MySQL

指定客户端密钥和证书文件,可设置

10.2.9

R E

. tl s.cer

t fi l es

0 9 选项创建账户 。要

t i s . c l ient.cr



db.

选项的



sl

- key

属性并启动

。然

服务器将拒绝连接

SL

选 Fab

后客户端还必须指 为

tl s . cl i e n t.keyfi

Fa

br

ic

CA

l e 的属性

服务端 。

P

服务端可以配置为连接到





LDAP

服务器



Fa

b r i c CA

服务端可以连接到

服务器来做下面的事情:

登录

前验证一个身份



口授权时获取 在配 置文



个身份的属性值

件中修改

。 LDAP

的配



来连接到一个

L D AP

服务器



ldap : # Enables or disables the LDAP client (default : false) enabled: false # The URL of the LDAP server u rl: : /

: : /base userfilter : atr

工 bute

:

# ’ names' is

a口 ary of stri 口 gs tha 工 dentify the speci f 工 # which are requested from the LDAP server . names : # The ’ converts 『 section is used to convert LDAP atr 工 bute # to fabric CA attribute values . C

attributes

values

# # # # # # # #

For example, the following converts a 口 LDAP whose value begins with ’ revoker ’ to a fabr named ” hf.Revoker ” with a value of ” true ” evaluates to true) . co

口 verts

' attribute CA attribute ( because the expression ’ uid

工 C

:

name : hf . Revoker value : attr ( ” uid " ) =~

” revok

食 ”

# # As another example , assume a user has an LDAP attribute named

ri c



10



Fabric CA

架构设计

与讲

•!• 解

has multiple values of ” dnl "' "dn2 '’ , and " d 丑 3 # ’ member 『 whic # Further assume the following c o nf i guration . # converters : # - name : myAttr # value : map(tr ( 叮 nemb e r ” ) ,” groups " ) # maps : # groups : # - name . dnl # value orderer # - name : dn2 # value : peer # The value o f the user ’ 5 ’ myAt t r ' attr i bute is then computed to be # ’l orderer,peer,dn3 ”. Th 工 s is because the value of ’ attr ( ” member ” ) ’工 # ” dnl , dn2 , dn3 ”, and the call to ’ map ' with a 2nd argume nt of # ” group ” replaces ” dnl " with " orde ” and ” d 口 2 ” with ” per ”. converters : name : value : maps : : - 口 ame : value :

247

S

其中: D schem

:可为

ldap

D adminDN

ldaps

:管理员的

D pas



:管理员的密码



LDAP

D port







:可选的

D filter

IP

LDAP



于用户



为用户名的



登录

D LDAPtrs



LDAP

”]









个区别

名。

与此类似,(

比如,(

uid=%s

email=%s

Fabric

) 会

搜索



)可以用于邮箱地址作



性,其中

a 位 r ( “ uid

那么用户将赋予’

”的



hf.Revokr false





fabric

”)=~“



CA









用户’

uid

'属性的值为’ 的用例



将与

true ,; LDAP

属性

revok





联的独特名称映射到

Fabric



户”’

。 那么这

’ LDAP

属性的值

否则

’。





fcaAtrNme

配给 uid

应值





个表达式,它的值将被分

器请求名为“



属性名称数 CA

’,是‘

性的值为’

LDAP 型





LDAP

‘ hf.Revokr 服

' 属

用于映射



属性转换为

,是

er '开头

’ hf.Revokr

63

服务器请求的

是一

LDAP

D maps

实体

LDAP

uid



LDAP

fcaExpr

『 revok



默认为



用户从

属性的名称; 味着用户从

ldaps

,把登录用户名转换为

名的

:用于将

假设是[“

389,



:代

D converts

认为











树的根,用于搜

:搜索时的过滤





ldap

D base

CA



器的域名或者

:可选的端口号,

uid





host:



或者



用户将赋

例如, 意

2 48



Hyperldg

Fabric

LDAP



描述的

达式语

源代码分析与深入解读



govalute



使用



/ github.com

扩展





govalut

:等于用户名称的变 :

: 一





2 个



个需要

个’



string

’ 类

. md

revok

LDAP

变量

:一个需要

*”之

和函









文字,

如下:

tru

巳,则返回第

二 adm

in

第一

个参

其中第

个参数;否则

函数总是返回’



, C=US

stri

工 at

,默



第二

认分隔符





。 换





参数是

串是‘





参数 。



个参

数 ,

是一

个映 算

或者用户具有以“

orgl 算

“ orgl .dept2 . ” &&

吨’类型的值,因此数运

如果它的计



”,那么下述表达式计

=~





”结尾的名称

工 on



数必须解析为布尔值

返回第

true (afil

属性名称

数是任何字符串



”属性的值为“

“ O=orgl,C=US ” I I

LDAP

符串中



O=orgl,

开头的隶属关系,且“

个字

数的字符串进行



例如,果用户具有以“

atr

是 。



3 个参数的函

结果为

第 一个 参 数

型的值

2 个参数的函 参

由于

/ MANUL



数的函

射的名字,用来对第一个

DN =~

/ master

柯:和诸如“

的特定

单 一

: 一 if



符串,用于将多个值连接到

函数总是返回





变量

l 或



D map

巳语

于用户隶属关系的

个需

个分隔符 atr

的运

/ blo

量。

D afilton 一

/ govalute

~ 类”之



D atr

/ Knetic

这定义了诸如“=

这是一个正则表达式

D DN

htps:



.dept2 ”

结果将为

true

attr ( ‘ adrnin ’

。 ‘ true ’ )

)=

符将不会用于构造表达式





如,以下不是有效的表达式:

value : attr( "gidNumber) >= 10000

&& atr(

" g 工 dNumber)

另外,下述表达式中引号里的正则可以被用于返回



value : attr (” gidNumber ” ) 以下是 com

10006


presp, err

e ndorsePpa



Tx

模块

…,

_ A

l ,在返回给交 一 >

打包成

orde

处理

b l ock

散播使用

的结果

易发

Propsa!Ren

gosip



()

->

节点,再经



ChaincodevkOrQuy

orde per

En

e 口 v ,



err

ve lope

过程省略

comiter

起端

->

->

orde

模块的过程,这里亦





( 8) comit

巳 r 向账本





中,

/ comn.g



A



be . Send (四川,发送给 block

,通过

( .. . ),获取交易

: = putils. CreateSignedTx ( .. . ) ,把返回的 形成的

entrBusySa

err = txs

工 ncode

列调用后会进入下一步

p 巳 e r / chainode

e 口=

),然

(),这一步相当于模拟执行了交易

e . call Cha

/ endors

Propsal

( 7 )在

中的

工 mulator

Invoke

” 分支从而调用

. CreateProposalResponse ( ... , results , ... ) , result

集,其被放入

中的

> pResp , err= e.endorseProposal( ... ,

, 一系 / sc

. go

n 中调

他交易会用到了其的

Procespal()

core

工 ls

txs

) 。

l eTransctio

nvoke

账户的数据放入了写集(其

.g o 中,

t , ... ) 一 > ca

B

hand

code_exam pl 巳 02

ExecutQry

-〉返回至

mulationRes

二 轮的





/ handler.go

()、

巳 RangeScltro(),

c o re /e ndorse

Results ()

chain

/ chainode

. GetSa

A

i m / handler.go

function =吐

core

入读集,把更改的

( 5 )重回

if

一系列辗转,会在

t xsimulator

B

/ cha in code/ sh

: = handler . cc . Invoke (stub) ( 即

后根据第

e/

stub,args) (该函数简单

转了一笔钱,即形成

” 1 。 ” ]}

e ndorse

/巳 ndorse.g

o nse Enve

chaincode invoke ” ,“

在交易返回时,再使用

如下:

chaincodeinvoke( . .. )函数向 ( 2 ) core

Vers



287

模块提交到账本对象,添加

examp

额,并由

调整后各自新的余额值)为例,步

( 1 )交

节点依据

中,

chainode

两个账户的余

l Resp

or

gosip

添加到

sa

orde

节点接收数据后,经

数据库中

po

中再发送给 ) 。

b l ock





ChaincodelvkOrQuy

,会将

终添

中的

中的

pe



。 Pro

数据直接打印, on

章账本机制的设计与实现

(交易模拟器)放入一个名为读写集的容中

r 统一段取,然后将读的数据放入

c om

1

提交

block

时,

最终定位



core

/ l edgr

/ kv

l edgr

/ kv

一 l edgr.o

中的

28



H y p e 巾

d ge

Comrnit(block)

r Fabric

源代码分析与深入解读

->

err =

l.txtmgmt . Comrnit ()



l.txmg

. Val

工 dateAnPrp(block

是使用交易管理者向

true) , ,

s tae

提交的易数据



ValidteAnPrp

做两件事,一是使用验证器交易的读写集以确定有效性;二若 则将

交易



做准备

写集

中的数据放入升级包,为下

一步的

l.t xtmg.Coi

()提交这批数据



交易模拟器/查询

11.3.2 交易模



interfac.go





TxSimulator



txmgr 和

kvLe

dger





功能,这点从 Exe

cutor

(),

T xS

以 在













TxSirnulato

除了查询,



txmgr

Query



query





接口可获取



TxSimulator

Helpr

实现了自身

TxSimulator





实际上包含 版本的

查询方面



Query

VersiondDB

还提供状

Executor

Executor,





),因此直接使用的

写入功能,人分为增

加和删除

涉及的操作就有查、增删

/ lockbasedtxmgr

/ lockb

写、删的操作步如下

lockBasedQuryExt

查询功能上的增强和拓展(其中交易器

TxSirnulato



参看

中的

couhDB



中的

()

是富查询接口,因此只支持

一般是交易模

中的

lp er.go

名字可 imulator

巳 cuter.go

core/ldg_

lockbased_tximur.g

NewQuryExcto

he

算是

ExecutQry

ulator

这个

的接口在

/ 下

_query_ex

Helpr

Executor

/ lockbasedtxmgr

两者均直接使用

q u ery Query

lockbased

N 巳 wTxSim

N ewQuery Executor 而

Query

定义,具体实现为

lockBasedTxSimutr 过账本

交易查询器



。 ased

_ tx_simulator. go



helpr.go

,交易模拟器的读、



( 1) Get State (口 和

key

,从

的版本)

stae



s ,

key)

这里的名字空间即

chainodeID 同



(口

key , value

,也即对应

s,

key, value)

),将一个值写入集



chain

code

的每一个交易使用

-> s . rwsetBuild.AToWS

(且

S ,



( 3 ) DeleteState(ns , 的





(2)Seta

key

q . helper.getState(ns , key ),依据名字空

数据库中获取一个状态,返回并写入读集(的是值

读写集,下

当给的

->

value



nil



key)

SetState(ns,

->

,即表示要将 此

key



值置为

nil

,也即

),

删除

要删除这个值

一个值





for . . . { SetState ( ... ) },一次

( 4) SetStateMultipl eKeys (ns, kvs ) • 性设置多个键值对,写入交易的集

nil

key ,



( 5 ) GetStateMultipleKeys ( ... ) -> q . helper . getStateMultipleKeys ( ... ) , 依据一个名字空间和批

key

( 6) GetSaR 工 terao

,从

口 geSca

stae

( ... ),依据-

口工

个命

数据库中获取一批状态

terao(

... ) -> q . helpr

名空

取一个指定范围的交易查询迭代器



间,根据开始的

key



结束的

.g

key

etSaR

,从

口 geSca

stae



数据库中获



( 7 ) ExecuteQuery (且

amespc

, query

)一>

q. helper . executeQuery

集 写

读 1.



q . helper . done (),交易查询器执行完毕

( 8) Done ()•

289

。 不支持这个接口

Je v eldb

(narnespace , query),

•! 章账本机制的设计与实现

1 第

入了一 时放

得到的数据暂 删

交易查、增 而是将

, 中

数据库 stae

直接写入 正的

数据并未真

交易 的时候,所形成

交易 因为在使用它处理

器,就是 拟

交易模 之所以叫

TxSimulator

。 个名为读写集的地方备用,因此是模拟交易



u erisKy rangeQ

和 rangeQuisMp

写集、 writeMap

读集、 readMp

多个值各自形成

。 /下)

/ kvrwset

/ rwset

/ ledgr prots

围读值(三者均定义在 范

Info RangeQury

写值、 KVWrite

读值、 KVRead

: 类数据

三 读写集存储

。 RWs

ns 单独有一个

code chain

,每个 key



chainodeID 映射,即以

1 sRW ]竹

map [string rwMap

个 一

,成员只有 RWSetBuildr

中的

。 builder.g

/ rwset_ rwsetuil

每笔交易都对应一个读写集,实现为 的

code chain

个 一

。 成的范围读集

口,都会将一个写值入 等接

() DeltSa

() 、 Seta

每次调用 TxSimulator

。 IsDelt



除的标 删

,还包含一个决定此写值是否为 value

和 key

,每个写值包含

: KVWrite 写值

TxSimulator

。 接口,都会将读取到的值并其写入交易集

() tSae

Ge 每次调用

。 和值的版本号,而不包含身

key 每个读值只包含

, KVRead

: 读值

交易的写集。

范围内的多个原始读值 。

两类数据,即原始的范围内多个读值或哈希

。 具

很容易理解,而范围内读值的哈希生成则需要借助其他工 当 回



这里 。

读值 个抽取

一 个

一 ()

Next 器的

用者使迭代 率低下),然后调

导致 的处理效 而

量过大

(这样可以避免数据 给调用者

息的迭代器返回 含了读值范围信

个包 一

调用者,而是将

打包返 集合

内所有读值 是直接将这个范围

时,不 一定范围数据的交易

用户发起读取

存储 Readslnfo

,而成员 一样

Key rangeQuy

中,其余的成员同 RangeQuryifo

。 是否结束

标识了 itrExhaused

对应的值), endky

,范围读值中不包含与 意

标识了范围至何处结束(注

, endKy 从何开始

标识了范围

, s tarKey 中

rangeQuyK 。

value 的

map 为

RangeQuery Info

key, 的

map 为

rangeQuyK 是以

rangeQuisMp 。

写入将在下文介绍 何时被

读值

个范围内所有读值的集合, 一

,每个范围读值是 RangeQurylfo

范围读值:

: 涉及两个迭代器

把迭代

chain

( 2 )在核心的 Query It 巳 rat

or



该迭代器的使用依附于(

code







包 TxSimulator

再次强调,



。 core

理范围内,为

1 )中迭代器所查询出来的结果

,而提 Executor

Query

在查询功能上主要的拓展之一 TxSimulator

对 Executor

Query 供迭代器也是

itrs 的

Helpr query

器记录在

,由交 result

中的 .go

/ helpr

( . .. )接口获取,每一个迭代器都会

工 terao GetSaRngc

易模拟器的

x mgr

/ lockbasedt txmgr

( I )在交易模拟器的范围内,为

/ chainode

/s him

. go

/ chainode



中的

Stae­

290



H yp e 出

2.

d ge r F a bri

c 源代码

分 析与深入解读

迭代器

result

s ltr





器管理了一个工具,



ResultHpr

rwsetu

t il / query

_r巳

。 RangeQurysltHp

内的







用 的



该值



go





一棵默克





, 该

, 读取



读值都会添





( ) 接







Level] []H 默

树也叫哈



a sh 克





树中有

树,

实现



步生成是



e /ledgr

决定)

query

_res u

时存储范围

的哈





hasingEbled





决定,

/ l e dgerconfi

。每

lt s _help



体进

该树,

个比





/ le d g

result

巳 r

迭代器



con fig .

调用

pendigRsul



怡和

extLafvlHsh





maxDegr

II

e=2



[x] - {hashN},



merk

l e­

哈希值



会进



Le

v el



希 成

has ,

l eTr



的值前后连



工具

map[Merk

标识树的 并

h l - 2 ,然后将

默克尔树的操

,由

­



行一 个归

h a sh2

ee



是哈希值 e 。



has

说明

中,一

maxDegr 时

l eTr







值的操作 一

: 个

整体

h l - 2 放入



级,







然后

一层(

merklT

也就 的

update



则连续插入

x 为





)函数 /设

e l 和 节点

merk

m e rkleT

h a shl



是举



中的 的

该操作是将



e r.go

每个节点保存

: Lev + I 个





下面也



Degr

s h2

希,得

l 2 )里面。



m ax

1 , ha





较重要 有

has



中实 现了

Levl

节点存有 L e ve





树中的

是 (口

格式



l ,当



于暂

对应



持有并管理

对这



RangeQurysltHp

RangeQurysltHp



RangeQury­







中的

pendigRsult cor

Enabled

helpr.go ingResult

存储与 同



s_ pend

步更新的(其实哈希值是否

eryRadsHhi

中暂存





置决定,该配默认开启由

IsQ u

Next ()

于存

加同

由账 本 的配

中的

Tre

中的

merklT

两者是随着数据

suit

l ~ Levl

值,

9 共

9 个哈希值

hasN



代表

~p

调用

N 的哈希值

9 次





merklT



tre

的变化为

update

接口

·

1) [1 ]- {hash l } 2 ) [1] - {hashl ,hash2} 3) [2] 立,

所有

- {h



a shl

向下

- 3 },

一层,







也就

3 个时

是第

,因

2 层





len(curt

L e v elHash)

集哈希值

3 { cha 工 nCodeTal : = args [l] queryKey : = args [ 2 l channel : = args[3] f : = ” query ” invokeArgs := toChaincodeArgs(f , queryKey) response : = stub InvokeChaicd(Tl ,工 nvokeArgs if response Status != shim . OK { errStr : = fmt Sprintf ( ” Fa 工 le d to invoke chaincode . Got Error ()) fmt

. Pr

sh



该方法与 是

( ” Que

shim

exampl02

方法



12. 7



qu

er

毛s

", err .

类 y

” \ ”, \ ” Amount \ ” : \ ””

+ string(event-

: 在 s \ n ”, j sonResp) []byte(jsonResp)) Respon

. Suces(

invoke



. Error(errStr)

工 m

jsonResp = string(response . Payload) } else { jsonResp = ” { \ ” Name \ ”: \ ” ” + event + Valbytes) + ” \ ” ) ” fmt . Printf

ero

channel )

工 ntf(erS)

return

retu

,

似,



方法,从

别在于调用

stub

exampl02

中进行状态的

. InvokeCha

in co d 查



e 方法时调用的



example05

巳 xample05



于它将查询获取到的

样也

A

是一

chainode func (t *S



个跨

、 B

码分析如下 i mpleC

c h a in



值记



了下来

code

调用

exam

ple02



案例,和

exa

mple04

的区别





: h a 工 ncode



工 nvoke(stub

sh i m. ChaincodeS tubinterface , args []string)

学习使



仅供非商业用

318

•!•

Hyp erledger Fabric

i 主或交流学习使用

源代码分析与深入解读

pb . Response { var sum, channelName string// Sum entity var Aval, Bval , sumVal int //value of sum var err error if len(args) < 2 { return shim . Error ( ” Incorrect number of argume ent

- to be computed

工 ty



Expecting atleast 2 ”) ts.

chaincodeName := args[O] S 山 n = args[l ] if len(args) > 2 { channelName = args[2] } else { channelName = ”” f : =” query ” queryArgs := toCha 工 ncodeArgs(f ,’ l a") response := stub .I nvokeChaincode(chaincodeName, queryArgs, channelName) 工f response.Status != sh 工 m . OK { errStr : = fmt . Sprintf (” Fa 工 led to query chaincode. Got error : %s ”, ponse.Payload) fmt.Printf(errStr) return shim . Error(errStr)

r es-

Aval, err= strconv . Ato i(string(response . Payload)) if err ! = nil { errStr := fmt . Sprintf (” Error retrieving state from ledger for queried chaincode : 毛 s ”, er.Eo() fmt.Printf (errStr) return shim.Error(errStr) queryArgs = toChaincodeArgs(f , ” b ”) response = stub . InvokeChaincode(chaincodeName, que 口 r Ar gs , channe lName) 工 f response.Status != shim.OK { errStr = fmt. Sprintf ("Failed to query chaincode. Got ero :在 s ”, ponse . Payload) fmt.Printf(errStr) return shim.Error(errStr) res­

Bval , err= strconv.Atoi(string(response.Payload)) i f err != nil {

errStr : = fmt.Sprintf("Error retrieving state from ledger for queried chaincode : 毛 S ” , er.Eo() fmt.Printf(errStr) return shim.Error(errStr) sumVal = Aval + Bval err = stub.PutState(sum , []byte(strconv.Itoa(sumVal))) if err != nil { return shim.Error(err . Error()) fmt . Printf ( ” Invoke chaincode successful . Got

sum

毡 d\n

”,

s umVal)

return

工 m

12.8

319

A 中获取到的



B

中(需要注意,跨链码调用如果进行信道则指明 code

chain

channe!N ame

•! 能合约案例分析

exampl02 的操作,将从

chainode

2 次跨

的值求和存入了当前

c h a in co d e 智

. Success ( [ J byte ( strconv . Itoa ( surnVal) ) )

方法中进行了 invoke



sh

章 12



盘成 m1

仅供昨附业

) 。

invokereturnsvalue 何对 案例主要用于展示如

invokertusal chain code

代码分析如下

。 行设定

智能合约调用的返回值进



func (t *SimpleChaincode) Invoke(stub shim . ChaincodeStubinterface) pb.Response ( _ , args := stub.GetFunctionAndParamete rs() // Entities var A, B string var Aval, Bval int // Asset holdings II Transaction value var X int var err error len(args) ! = 3 ( 工f return shim . Er ror (” Incorrect number of arguments. Expecting 3 ”) A= args[OJ B = args[l) Avalbytes , err := stub.GetState(A) { if err != n 工 l return shim . Error (” Failed to get state ”) if Ava l bytes == ni l { return shim.Er r or("Entity not found") Aval ,

= strconv. Atoi(string(Avalbytes))

Bvalbytes , err := stub . GetState(B) i f err ! = ni 1 { re t urn shim.Error ( ” Failed to get state ” ) if Bvalbytes == nil ( return shim . Error (” Entity not found ”) Bval, _ = strconv . Atoi(string(Bvalbytes)) X, err= strconv.Atoi(args[2)) { if err != n 工 l 工 on transc nvalid (”工 return shim.Ero Ava l = Aval - X Bval = Bval + x err = s tub .Put State(A , err != nil ( 工f []byt

e

(strconv

. 工 toa

amount , expecting

(Aval) ) )

a

工 ntegr

value ” )



流学习使用

仅供#商业用途或交流学习使

•!•

320

H yp erl edger Fabric

return

源代码分析与深入解读

sh

工 m

. Error(err . Error())

err = stub.PutState(B , []byte(strconv . Itoa(Bval))) if err ! =nil { return sh 工 m . Error(err.Error()) return shim.Suce([]bytf

. Sprintf

链码调用的返回值可以被封装在

( ” {电

s him.Suc

参数

即为

客户端

收到的

中的

paylod

, 宅 d

}”,

Bval))) Aval,

()方法中,该传人的[

] byt

e



map

12.9 map

这个

操作

respon

es

d





展示了关于组合键的存





范围查询以及调用

couhdb

进行富查询的

。 chainode

代码分析如下:

func

(t * SimpleChancod function, args : = switch function { case

'’

put ":

cas e



remove "

)工 stub

nvoke(stub

sh

. GetFunc

工 m

工 onAdParmets

.ChaincodeStubinterface) () pb.Reso

{

口 se

case "get " case



keys ’· .

case ” qiJery ”: case ” history ”: default : return shim . Success ( []byte ( ” Unsupported operation ” ))

该链码主要分为

6 个功能

1.

put



方法



该方法除了调用之前介绍的

CreatompsiKy

PutSae

用于

Key

值的生成

进行数据存取外,还调用了



建组合键

的函



if len(args) < 2 { return shim . Error( "put operation must include two arguments : [key , value ] ”)

l o fij 仅供



ch ain c od

章 12

•!

e 智能合约案例分析

key : = args [ O] value : = args[l] if err : = stub . PutState(key , []byte(value)) ; err ! = nil { err) 屯 s ’', fmt.Printf( "Error putting stae operation failed . Error ( ” put 工 ntf . Spr return sh 工 m . Ero(fmt 5 :毡 stae

才 ~ m

堆成交流学习

321

工 ng updat

er) ”,

: = ” compositeKeyTest " 工 ndexNam compositeKeyTestindex , err := stub . CreateCompositeKey(indexName , string{key}) if err ! = nil { return shim . Error(err.Error( ))

[]

valueByte : = []byte{OxOO} err : = stub . PutState(compositeKeyTestindex, valueByte) ; e rr ! = nil { 工f ) e r ”, 革s 工 teKy fmt . Printf (” Error putting state with comps return shim . Error ( fmt . Sprintf ( ” put operation failed . Erro r updating err)) : 毡 s ", state with compsiteKy return

方法

2. remov 法 该方

. Success(nil)

工 m sh

ate St

Del 调用

i f len(args) < 1 { return shim . Error ( ” remove

。 操作

删除 值的

进行键

方法

工 on operat

must include one argument : [key ] " )

key : = args[O] err : = stub . DelState(key) if err != n 工 1 { return shim . Error(fmt.Sprintf( " remove operation er) 毡 S ” , : updating stae

fa

工l

ed .

Error

丁 一

作 操 询 查

•L a•L e

CU



nue

←」

法 方 该

TH Rη +



9

立 工 f

方 调

return shim . Success (nil)

len(args) < 1 { return shim . Error (” get operation must include one key : = args[O] value , err : = stub . GetState(key) if err ! = nil { return shim . Error(fmt . Spr in tf ( ” get state : %s ” , err)) h al . Mars son jsonVal , err : =〕 ret urn shim . Success ( jsonVa l)

(str

工 ng

operat

(value ))

工 on

fa

工 led

山 arg

.

net

,

Error

a key ” )

a ccessing

使



•!•

322

H yp erle dger F a b r i c 源

4. keys ke

码分

析与

深入解

i卖

方法

y s 方法

字典序



排列



于进行范围查询

GetSaByR

口 ge

,根据传人的

st a r t K 町、

end

Key

进行



if len(args) < 2 { return shim . Error( "put operation must and value ”)

include two arguments,

a key

startKey := args[O] endKey := args[l] stime := 0 if len(args) > 2 { st 工 me , 一 = strconv . Atoi(args[2]) keysiter, err := stub.GetStateByRange(startKey, endKey) 工 f err !=nil { return shim.Error(fmt . Sprintf (” keys operation failed. Error accessing stae

:屯

s

” ,

er)

defer keysiter . Close () var keys []string for keys I t er. HasNext () 工 f stime > O { t工

{

* me.Slp(tiDuraons)

t 工 me

. Millisecond)

response , iterErr : = keysiter.Next() 工f iterErr != n 工 1 { return shim. Er ror ( fmt. Sprint f (” keys accessing stae : 革 S ”, er)

operation

failed .

Error

keys = append(keys , response.Key) for key , value := range keys { fmt . Printf (” key 奄 d contais

革 s \ n

” ,

value) key,

jsonKeys , err : = json.Marshal(keys) i f err ! = nil {

return shim. Error ( fmt. Sprintf (” keys shaling JSON :屯 s ”, er) r e turn

5. query 用 等 q

且 ery

failed .

Error mar -

口 Keys)

方法

该方法 couhdb

shim.Suce(jo

opera t ion

s taedb

G etQuryR

esu ,不支持

lt le v el db



行一个富查询操作,该接

口只

针对提



富查





作的



: = args[OJ keysiter, err := stub . GetQueryResult(query) i f err != nil { return shim.Error(fmt.Sprintf (” query operation failed. Error accessing



s ta

te

:毡

s

”,

12



c h ainco

d e 智能

合约

案例分析



32

er)

defer keysiter . Close() var keys []string for keysiter.HasNext() { response, iterErr := keysiter.Next() if iterErr ! = nil { return shim.Error(fmt.Sprintf (” query access i ng stae : 奄s ”, er) keys

=

operat

工 on

failed.

Error

append(keys, response . Key)

jsonKeys , err := json.Marshal(keys) if err != nil { return shim . Error(fmt . Sprintf("query operation failed. Error marshaling JSON

:毛

S ”,

er)

return shim . Success(jsonKeys)

6. history

方法

值每

该方法用于获取键值对的历史状态变化,通过调 一

GetHisoryFK 次变化所对应的

txID

方法获取

key



key := args[OJ keysiter , err := stub . GetHistoryForKey(key) if err != nil { return shim . Error(fmt.Sprintf ( ” query operation failed . Error accessing state : 奄 s ”, er) defer keysiter . Close() var keys []string for keysiter.HasNext() { response, iterErr := keysiter.Next() if iterErr ! = nil { return sh 工 m .Error(fmt.Sprintf ( ” qu e ry o peration aces

工 ng

stae

:毡

S ”,

failed .

Error

er)

keys = append(keys, response . Txid) for key, txID := range keys { fmt . Printf ("key 革 d contais

在 s \口”,

jsonKeys , err : = json . Marshal(keys) if err != n 工 l { return shim . Ero(fmt.Sp 工口 tf ( ” query JSON

return sh

:毛

工 m

5

”,

er)

.Success(jsonKeys)

key

,

tx

工 D)

operation failed . Error marshaling

324



H y p e rl e d ge r Fabri

c 源代码分析与深

入 解读

marbles02 12~0 marbles02

是一

链上进行

资产

的创

个大理

chainode

建、

石 转



移、查

产 询

管理的

案 等操



例,介绍了如何在



能合约中





资产



在 区块



代码分析如下:

type marb le struct { ObjectType string 、 j son :” docType " 、/ /docType is used to d 工 s t 工 ngu 工 sh the var 工 ous types of objects in state database Name st r 工口 g 、 j so 口 : ” name ”、 //the fieldtags are needed to keep case from bouncing around Color str 工 ng 、 j son : "color ” 、 Size int 、 json : ” size " 、 Owner str i ng 、 json :” owner " 、

称、颜

该智

能合约定义了





一个

尺寸



拥有





理石的



构体







存储相

关实

体的信息,包括了



型、





func (t *SimpleCha 工 ncode) Invoke(stub shim . Cha 工 ncodeStubirfa) function , args := stub . GetFunctionAndParameters() fmt. Println (” invoke is running ” + function)

pb . Response {

II Handle different funct i ons if funct 工 on == ” initMarble ” { llcreate a new marble return t . initMarble(stub, args) } else if function == ” transferMarble ” { llchange owner of a specific marble return t . transferMarble(stub , args ) } else if function == ” transferMarblesBasedOnColor ” { lltransfer all marbles of a certain color return t . transferMarblesBasedOnColor(stub , args) } else if function == ” de l ete ” { lldelete a marble return t . delete(stub, args) } els 工 f function ==” readMarble ” { llread a marble return t.readMarble(stub , args) } else if function == ” queryMarblesByOwner " { I lfind marbles for ow 口 er X using rich query return t . queryMarblesByOwner(stub , args) } else i f funct 工 on == " que 巧 rMables ” { llfind marbles based on an ad hoc rich query return t .queryMarble s( stub , args) } els 工 E function == “ getHistoryForMarble ” [ llget history of values for a marble return t . getHistoryForMarble(stub , args) } else if function == ” getMarblesByRange ” { llget marbles based on range que 巧 F return t . g e tMarb l esByRange(stub , args) fmt . Pr 工 ntl ( ”工 nvoke did not find func : ” + function) return shim . Er r or ( ” Received unknown functio 口 invocat

llerror i on

” )





智能合约主

1.



包括了

intMarble

12



c h a incode

智能合约案例分析



325

9 个方法:

万法

创建



fun c

个大理



(t

信息

,并



入 账本



in 工 tMarble (stub sh i m. ChaincodeStubinterface , pb . Response { var err error if len( args) ! = 4 { return shim . Error ( ” Incorrect number of arguments . Expecting 4 ” ) str

* S工

mpleChaincod)

args

[]

工 ng)

fmt . Println ( ”- start init marble ” ) len(args[O ] ) INFO 001 Loading conf igurati o 口 2017 06 12 21 : 01 37 644 EDT [cornmon/configtx/tool] doOutputChannelCreateTx -> INFO 002 Genrat 工且 g new channel conf 工 gtx 2017-06-12 21 : 01 : 37 645 EDT [common/configtx/tool] doOutputChannelCreateTx -> INFO 003 Writ 工 ng new channel tx

################################################################# Generating anchor peer update for OrglMSP

#######

##########

################################################################# 2017 06 12 conf

21 : 01 : 37 . 674

EDT

[cornmon/configtx/tool]

main

>

INFO

001 Load

工口

g

工 guration

2017-06-12 21 : 01 : 37 . 678 EDT [common/configtx/tool] INFO 002 Generating anchor peer update 2017-06 - 12 21 : 01 : 37 . 679 EDT [comrnon/configtx/tool] INFO 003 Writing anchor peer update

doOutputAnchorPeersUpdate

->

doOutputAnchorPeersUpdate

->

################################################################# Generating anchor peer update for Org2MSP

#######

##########

################################################################# 2017-06-12 21 : 01 : 37 . 700 EDT configuration 2017-06-12 21 : 01 : 37 . 704 EDT

[common/configt:x/tool] [comn/f

工 gtx/ol]

INFO 002 Generating anchor peer update 2017 06 12 21 : 01 : 37 . 704 EDT [comrnon/configtx/tool] INFO 003 Writing anchor peer update





步生成我们各种网络实体的所有证书和密钥,

务,以及配置

gen

Chanel

13.2.4 接下来,你可以使用命

所需要的



组交易配置集合

main

->

INFO

001

>

doOutputAnchorPeersUpdate

->

s is

block

用于引导



3

网络



再次提示你是否继续

工 ng

doOutputAnchorPeersUpdate

启动网络 令来启动整个

Load



回答

y :

序服



•!•

338

H y p e rl edger Fabric j 原代码分析与深入解渎

. /byf 口 .sh -m up Start:.ing with channel ' π1ychanel 『 and CLI timeout of ’ 10000 ' Continue (y /n) ?y proceeding Creating network "net_byfn " with the default dr iver Creating peerO orgl . example com Creat 工 ng peerl.orgl . example com Creat 工丑 g peer0 . org2 . example. com Crea ti 口 g orderer . example . com Creating peerl.org2 . example . com Creating cli





l

\ 一-

\ 一)

l

||

|

|一

一 \ I ) I

/\



\

I

||

一-



/ 一/

||

\ 一 \|

|| ||




DEBU

Return

004

工 ng

existing

> DEBU 005

工 gnidety

Obta

工 n 工 ng

default sign 工 ng identity 2017-05 - 16 17 : 08 : 01 . 366 UTC [msp/identity] Sign - > DEBU 006 Sign : pla 工口 tex : OAB 1070A6708031AOC08F1E3ECC80510 . .. 6D7963631AOAOA0 571 756572790A0161 2017-05 16 17 : 08 : 01 367 UTC [msp/identity] Sign - > DEBU 007 Sign : digest: E6 1 DB3 7F4E8BOD32C9FE10E3936BA9B8CD278FAA1F3320 B08712164248285C54 Query Resul t ’ 90 2017-05 - 16 17 : 08 : 15 . 158 UTC

===================== =====================

Query

I 一 |

I

你可以滚动这些日志去查看各种交易

|一



|

\

|

|

|

[ma

main -> INFO 008 Exiting . .

工 n]

on

PEE R3

I I\



l 一|

I

\ 一|

I

|一

channel

' mychanel



All GOOD, BYFN execution completed



- ||\||||||

I_

on

I 一|







工 S

successful

========== ======== ===





13

Fabric-smple

项目分析与实践



39

关闭网络

13.2.5 最后,让我们 把脚本

全部停下来,这样我

们可以一步

将关闭你的容器,移除加密材料和

4 个配 你将再



一次被

提示是否继续,回答



一步

信息,并且从

地探索网

Docker

络设置。以下操作 仓库删除

chainode

镜像



y:

. /byfn . sh m down Stopping with chan 口 el ’ my cha 口 el ’ and CLI timeout of ' 10000 ’ Continue (y/n)?y proceeding WARNING: The CHANNEL_ NAME variable is not set . Defaulting to a blank string . WARNING: The TIMEOUT var 工 able is not set . Defaulting to a blank string . Removing network net_byfn 468aaa6201ed Untagged: dev-peerl.org2 . example . com-mycc-1 O: latest Deleted: sha256:ed3230614e64elc83e510c0c282e982d2b06d148blc498bbdcc429e2b2531e91

在接下来的章节中,我们将介绍构建功能齐全

Hyperldg

要求和步骤

fabric

网络的各种



13.2.6

加密生成器

我们将使用

cryptogen

工具

为我们生成各种网络实体的加密材料(

x.509

证书)

。这 些

加密材料是身份的代表,在我们网络实体进行交流和易时这些用于签名/验证

。 Cryptogen

消费一个包含网络拓扑的

crypto-nfig.aml

织和属于这些组的件

生 cert

CA

),它将特定组件(

一组

pers

证书,我们正在模仿



Hyperledger Fabric

成 和

ordes



k eys ) 的

count

tore

c a



per

如果你有兴趣,可以自己学习



我们现在不会深入研究

x.509

的数量;在我

证书和公钥基础设施的细



在运行该工具之前,让我们快速浏览一下这段代码 意在

了唯一的根证书(

中的实体私钥签名,然后通

变量,我们将使用它来指定每个组织中 per





)绑定到该组织。通过为每一个分配唯的

和通信是过存储在

们的例子中,每个组织有两 节

个组织都配



( s i gncerts

在这个文件里有一

。每

们为组

个经典的网络,这中成员将使用自己证书颁发机构

中的交

过公钥手段进行验证

证书和密钥

,并允许我

crypto-nfig Ordegs

头下的

Name



Domain



Specs

参数

. yaml :

OrdererOrgs :

#- - - - - - - - - ------------------- ---- < - - - - - - # Orderer

#------------------------------------- Name : Orderer Doma 工 n : example com



特别注

•!•

340

H y 严

rl e d ger

Fabric

#-------------------------------------------# ” Specs ”- See PeerOrgs below for comple t e descript ion #------------------------ - ------Specs : - Hostname : orderer # ------------------------- -

# ” PeerOrgs ” Defin



- -

- -白

of organizations

工 tion

# --------- -- --- -----



mang

peer nodes

工 ng

----------------------

- ------

PeerOrgs #-----------------------------------------# Orgl #----------------,,-------------------- Name : Orgl Dorn a 工 n : orgl.example.com 网络 实 体的命名约 定

作为参考点,它与 Or 还可以 参考

如下:”{

d er 的

Membrship

MSP ID

相 关联



cryptoge

m a in }

所以使用我们的排 序节点

。 ) ,以便更深入地了解

口工具,生成

MSP





的证书和密钥将被保存到名为 crypto-nfig





配置交易生成器

13.2.7

configtxgen tol 用于

创 建

4 个配 置工作

: ord

e r 的

channel configuration transaction, *以及两个 ( 一 个对应

一个 Pe

巳 r 组织

Cha

order block 在

C hanel 创

是一个 ordeing

orde ch

Configtxe 排

使用

序服务组织

ane



Orde

p er 节点

请特

。 别

区块

注意 此文件顶部的"



per

个 Sampl



。 具有

3 个成员: 一

每个组织管理

2 个

的联盟,由上述两个节点组织构成 。 一 个是 orde 的创世

TwoOrgsChanel

工作的时候它



们将作为传递的 参数 由渠道

。 级别配直

管道存在于联盟的范围内,所有必须定义整个网络

文件 。

加 . exampl.co 和 per0

参数 : . org2

首先,我们为每个组织指定了锚点节 . exampl



和持有

系统制的旧文件中定义,然后

文件还包含两个值得注意的附 . orgl

,正如名称所示

个独特的标题:

另一个是针对管道的

建我 们的

onsrtium

) 。

里有两

们创



文件 ium

”部分,这 ;





eConsrt

Profiles



ions

configtx.yaml

- TwoOrgsdeGni

引用

transc

peer

A nchor

transcio

Orgl&2 一

S 叫时



) 。

chanel

anchor

含示例网络的

该文件还指定了

主我们的

chanel

peer transactions

巳 l Configuration ( configtxe

。可以及两个节点组织(

这些标题很重 要,因为在我 。

block,

anchor

的创世区块,



l 上的

一个

n

servic

建的时候广播给

指定了每个组织在此

gensi

) 。

有关此工具的完整说明,请参阅

(perO



该文件包含了有关定义和语法的大量档

Service Provides(MS

我们运行

文件夹中

{.Hostname } }.{Do

. com

) ; 其次

,我们为 每个成



员指定

MSP

文件夹,用来存储每个组织在 的概念

Fabri

c-s



现在任





amples

项目分析与实践

block

genesis

ordeing

servic

•!

341

中指定的根证书



通信的网络实体都可以对其数字签名进



运行工具

13.2.8 你可以用

con

你也可以



figtxen

试使用

矛 1 cryptoge

byfn

. sh

必要的话,你可以

. yam

里我

们也

口命令来手动生成证书/密钥和各种配置文件

。 byfn.sh

脚本中的

函数去生成定义在



c ryptoge 旦 工 /b

口 eratCs

然而,为了方便起见这



首先,我们来运行

..

ge

l 文件中用于你的网络配置相关证书

会进行相应介绍

要提供



脚本来完成你的目标 参考

crypto-nfig





orde

这是一个 关键 行验证

13

这个

具所在的相对路径

工具。

我们的 二

进制文件在

bin

目录中,所以我们

。 generate - config= . /crypto config . yaml

工 n/cryptoge

你可能会看到以下

警告

,这是无害的请忽略它:

[bccsp] GetDefault -> WARN 001 Before Falling back to bootBCCSP.

接下来,我们需要告诉

configtxe

告诉它在我们当前所工作目

us

BCCSP , please call In i tFactories() .

工 ng

工具需要提取的

configtx.yam

l 所

在的位置



我们会

录。

首先,我们需要设置一个环境变量来告诉

configtxe

然后,我们将调用

configtx

en

工具去创建

ord

export FABRIC_ CFG_PATH=$PWD lb 工 n/cofigtxe -pro f 工 l e artifacts/genesis.block

你可以忽略有

去哪里寻找 er

eris

)和

. yaml



genesis block:

TwoOrgsOrdererGenesis

关中间证书、撤销列表(

示例网络中使用其的任何一个

er

c onfigtx

MSP

-outputBlock

配置的日志警告

. /channel-



我们没有在

。 接下来,我们需要创建 NAME

chane 或者将

CH

ANEL

_ NAME

l

transcio

配置



请确保替换

设置为整个说明中可以使用的环境变量

$CHANEL

_



export CHANNEL_ NAME=mychannel # this file contains the

def 工 n 工 tions for our sample channel /bin/configtxgen - profile TwoOrgsChannel outputCreateChannelTx artifacts/channel . tx -channelID $CHANNEL NAME

接下来,我们将在正构建的通道上定义

Orgl

NAME

已被替换或 . /bi

者为

以下命

令设置

了环

境变量



anchor

per



请再次确认

/channel -

$CHANEL_



口/ configtxe profile TwoOrgsChannel -outputAnchorPeersUpdate . /channel artifacts/OrglMSPanchors . tx -cha 口 elID $CHANNEL_NAME asOrg OrglMSP

.>

342

Hyperledger Fabric

源代码分析与深入解读

现在,我们将同

一 ..

个通道定义

Org2



anchor

peer :

工口/ configtxe -profile T 气 t10rgsChanel -outputAnchorPeersUpdate . /channelartifacts/Org2MSPanchors . tx -channelID $CHANNEL NAME asOrg Org2MSP /b

13.2.9 启动网络 我们将利用

d ock

er-co

mpose

脚本来启动我们的区块链网络



件利用我们之前下载的镜像,井以生成

g ensi

s.

block



docker

- compse

引导

ord



er



working_ dir : /opt/gopath/src/github . com/hyperledger/fabric/peer # command : /bin/bash -c ' . I scr 工 pts/cr 工 pt.sh ${CHANNEL_ NAME}; sleep $TIMEOUT ’ volumes

如果没有注释,该脚本将在网络启动时执行所命

令,正如

所描述的那样







退出



地为

而,我们想

手动执行命令,

TIMEOU

以便

传递较高

的值

我们在



后发

公开每个调用的语法和功

生 能

(以秒为单位);默认情况



CLI

的情况中



容器将在

60

秒之



。 启动你的网络



CHANNEL_ NAME=$CHANNEL_ NAME TIMEOUT= compose-cli . yaml up -d



果要实



需要 打开

查看

你的



第二个终端来执行

1.

C LI

pe

4 个环境变



贝到





的日志,

请不要提供

- d 标志



如果你

需要

-f

docker-

日志流





令。

环境变量

为了使针对



区块链

docker-compose

CLI

rO

. exampl

来介绍我们的命令 此我们

orde

. com



容器中,因

点或者

. orgl

,则



不需要复



perO.o



CLI

制它们

。然

要相应地提供这些值



命令起作用,我们需要使下面给

l . exampl 而,如

检查

. com

涉及的这些变量将被拷

果你发送调用请求到其 docker

他的 compse-ba

. yam

l

per

节 中的



体路径: #

E 口 viro

me 口

t

variables for PEERO

CORE_PEER_ MS PCONFIGPATH= /opt/gopa th/src/g i thub . com/hyperl edger If abr i c/peer I crypto/peerOrganizations/orgl.example . com/users/Admin@orgl . example.com/msp CORE PEER_ ADDRESS=peer0.orgl . example .com : 7051 CORE PER_LOCAMS 工 D = ” OrglMS P” CORE_ PEER_ TLS_ ROOTCERT_F ILE=/opt/gopa th/ s rc/g i thub . com/hyper 1 edger If abr ic/peer I crypto/eOga

2.

我们将使用

口 izatons/rgl

. exampl.co/rsOg

.c

创建&加入信道

docker

exc

docker exec -it cli bash

命令进入

CLI

容器



om/tlsca

. crt







运行如果成功,你将

会看

到下列

工 thub

我们使用

configtxe

部分传递给

s 项目分析与实践



343

/peer#



el

. tx

,并将这个配置作为请求的二



。 TLS

握手

我们使用一

这是

orde



rot

cert

的本地

路径,允许我们去验



c 标志指定

文件名是

c -sample

工 C

cha

盖一州时作为命令的部分 证

Fabri

. com/hyperldgfab

工具生成信道配置-

orde





信息:

rot@Od78b6930:/pgahsc



13

chanel

chanel

. tx

的名

,当然你



,使用-

f 标志

指定

也可以使用不同的名

配置交易



称来挂载你自

己的

在这个例子中配 交易配置



export CHANNEL_NAME=mychannel 工 s mounted 工 n the channel-artifacts directory w 工 thin # the channel.tx file CLI container # as a result, we pass the full path for the file # we also pass the path for the orderer ca-cert in order to verify handshake # be sure to replace the $CHANNEL_ NAME var 工 able apro 工 ately

your

the TLS

peer channel create -o orderer . example . com : 7050 -c $CHANNEL_N!l.ME -f . /channel artifacts/channel . tx --tls $CORE_PEER_TLS_ENABLED --cafile /opt/gopath/src/ github

. com/hyperldgfabit

/ord

er

erO

gan

工 zations

/e

xample.co/

orderers/orderer.example com/msp/tlscacerts/tlsca example.com-cert . pem

此命

令返回一个

含了



chanel 。



. tx 剩

世区块一<

chanel-ID

中的配置信息

下的命令

. block

>,我



将使用它



入信道



它包



将会留在

CLI

容器内执行



当目标节最

以外的节,点时你必须记住所有命令

是除了

perO.ogl

须在相应的环境变量

.exam

现在让我们加入

perO.

org I . 巳 xample.co

下执行



com



频道。

# By default, th 工 s jo 工 ns 、 perO . orgl . exampl.co 、 only II the . block was returned by the previous command peer channel join -b

你可以修改

4 个环

3. 本节

境变量来让别的节点加入信道



安装和实例化链码 我们将利用一个现有的

简单链码,来学习如何编写自己的

应用程序和区块链账本会通过



以及背书我们

chainode 首先,将示

交易的

例代码

per

安装到



点上安装

相互 c hain

4 个

pe

巳 r 节



code

中的







。因



然后在信道上实例



我们

需要

个上





下面这个命



chain

在每 将源代码放到

code

个会执行 。

per

•!•

344

Hype



dger Fabr

节点的文件系统中

i c 源代码分析与深入解读

。 peer

cha 工 ncode install -口 myc -v 1 . 0 -p github . com/hyperledger/fabric/examples/ chaincode/go/chaincode_ example02

接下来,在信道上实例化

c haincode

略,为目标

per

chain cod

巳交易

节点启动

一个

需要被验证 令



,我



个背书)

容器(注意



P

们只



,设置链码 - P 参数)。我



的背书策

需要

指定的

、当

这个

策略。

们指

策略。这意味着我

这将初始化信道上的链码

chainode

的时候背书

在下面的命 作为背书

c

需要

如果我们将语法变为

" OR

('O rgOMSP .membe r

orgl

AND

或者

org2

组织中的一个

那么我们就

F

,’ OrglMSP.member ’ ) ” 节点的背书即可

需要

( 即只

2 个背书者





# be sure to replace the $CHANNEL_NAME environment variable # if you did not install your cha 工 ncode w 工 th a name of my cc ,

then mod 工 fy that as well peer chaincode inst a 口 t 工 a te -o orderer . example . com : 7050 - -tls $CORE _ PER_TLS 一 ENABLED --caf i le /opt/gopath/src/gi thub . com/hyperledger/f abric/peer/crypto/ ordererOrganizations/example.com/orderers/orderer.example . com/msp/tlscacerts/tlsca . example.com-cert . pem C $CHANEL _ NA 阻 n mycc -v l. 0 -c ’ {吐 rgs ”: [ ” int ”," a ”," 10"' ”b ",” 200 ”]} ’ P ”OR ( ' OrglMSP . member ',’ Org2MSP.member ’ ) ” argume

4.



t

查询

让我们查询

一下

a 的值,以确保链码

被正确实例化、

stae

DB

被填充



查询的语法

如下: # be sure to set the peer chaincode query

5.

C and -n flags apro 工 ately C $CHANNEL_ NAME -n mycc -c

’ { ” Ar gs ”:

[ " query

” ,”

a ” ]}



调用

现在让我们从

a 账户转

10

元到

b 账户



这个交易将创建一新的区块并更

stae

DB



调用语法如下: # be sure to set the C and -n flags appropriat ely peer cha 工 n code invoke -o orderer.example . com : 7050 --tls $CORE_PEER_ TLS_ ENABLED ca f i 1 e /op t/gopa th/ s rc/g it hub . com/hyper 1 edger If abr i c/pe er I crypto/ordererOrganizations/example . com/orderers/orderer . example . com/msp/ tlscacerts/tlsca.example com cert . pem c $CHANNEL NAME n mycc -c ’{ ” Ar gs ”: [” invoke ”,” a ”,” b ”,” 10 " l}'

6.

查询

让我们确认下



用的时候转移了

10

元给

之前的调用被正确执行了



b 。

因此查询

a 应该展示

我们初始化了

90

a 的值为





查询的语法如下

# be sure to set the C and -n flags appropriately peer chaincode query -C $CHANNEL_NAME -n mycc -c ' { "Args ”:

我们应

该看到以

下内容:

10

,在上



[” query ",” a ” ]}’



次调



Query Result: 随时









生了什

键值对和随后

的调用

com

中使用了

p ose



./ byfn

示去启动

脚本中没有注释掉的

. sh

你的网



block

-,

这个

的信道配



这个脚本驱动了

­

chanel.tx

文件



per

_ cha 口 ne

节点的文件系统中,

l _口

同时包

含了

ame>

.

chanel.tx



这个命



4 个

介绍了

per

per

节点执行

,作为之前产生

节点如何加入 去创

口现在我们有了由

4 个 Chanel

配置文件

per

节点以及

_ cha

建一条链



el

_n

gensi

b l ock

ame



的输

以及如何利用



2 个组织构成的

信道





是我 们的

T woOrgs­



D perO.oglxamc



xample

. com

系是通过



perl

. orgl

perl

. org2

c rypto-cnfig

件中被指定



< your

.bok

.e

docker

命令此

< your

命令被



5

文件

creatChnl

以及信道 配置的

区块被存储在

D joinChael

口这些

34

相同的

容器中

l name

创世



org2



i .yaml

命令的产出是一个创世区块-



目分析与实践



chane

creatChnl





井确保了命令执行成功,然后使用

CLI

令使用提供的



es

docker-mpsl

down

-111

D script.sh j 向本被拷贝到 命

mpl



script.h 景,其

c-sa

么?

注意:以下步骤描述了在 中的场

Fabri

90

重新开始并操

7.

1 3 章

. exampl

. com

. exampl.co

. yaml



属于

定义

的,

MSP



Orgl;



Org2

peerO .

径在



docker compse





D OrglMSP(peerO. orgl . example . com com

) 的

MSPa

nc

anchor

更新

pers

hors.tx



将在

) 和

后续被更新



Org2MSPanchos

Org2MSP(

per0

我们通过携带

. org2 . example .

. tx

chanel 配置

的名字传递

到排序服

Orgl

务来实



a n c h or

­

per





D

一个链码-

chainode

per0

. org2

_ exampl02 . exampl

口这个链码在

. com

per0

. org2

信道上,并启动



. com

节点对应的

容器

[ ” a ”,

10

perO 中被

程 同样为





。 ” ,

策略传

实例





实例



a 的 perO.ogl

查询发往



” b ”,

20

。 ”]



实例

化的结

关参数



. exampl

. com



策略被定义为一

,因

. exampl.co 此这

P 。

次查询将启动一个名为

键值









dev­

。 叮

Orgl

. orgl



果是一 个名为

. 0 的容器启动了

递相 perO

om

化过程将链码添加到

member ' , ' Org2MSP . member ’ )”,意思是任何交易必须被 口一个针对

.c

,同时初始化和链码服务有

peer0.o rg2 . example.com myc-1 口实例化过

. orgl.examp



. exampl

per 的初始化值是

被安装在

R

( ' OrglMSP .

或者

链码服务已经被安装在

Org2 dev-prO.

背书



•!•

346

Hyperledger Fabric

or

gl.

exa

mp

源代码分析与深入解读

l e.

co

m-yc1.0

的容器

作出现,因此查询的结果将为 q

10 一次

Q

invoke

被发往

链码服务被









que

第三

ry

装到

rO. 往

,这正说明

e . c om

ple

. org2

.co

e rl.

p le

or

,从

a 转移

. com

用于

g 2 . exampl

a 的值之前被转移了

10

10



L

e.





a 的

com-y





c -1

这将启动 . 0 。

返回

a



这指明了什么? 写



per

节点的链码服务



服务执行的情况(例如查询

操作,链码服务必须被安装在

per

器除了山

it

a 的值

初始化或者传统的交



) ,在其他情况下都不会启动



然,所有信道中的节点都持以块形式顺序存储

、 据库,以此来保存前状态的快照这包括了没有

在其 org

因为没有写操

m 。

. exam

v -pe

为了能够正确地在账本上进行读 每



a m pl

. e xam

p e erl de

90

.ex

. o rg2

请求被发

查询的结果也将被返回



orgl

perl

个链码服务,其名为

的值为 8.

pe



2 . e xample

例化了

. com

如上所



) 。最

不可



上安装



节点上

- 读/



。 - 针

交易导致容器的启动

此外,

对该链码





的账本精确备份及状态数 码服务的

pe

巳 r 节

点(

p e e rl

后,链码在被安装将是可达状态因为它已经

. 实

。 9.

如何查询这些交易?

检查

CLI

容器的日志



docker logs -f cl



你应该看到以下输出



2017-05 - 16 17 : 08 : 01. 366 UTC [msp) GetLocalMSP - > DEBU 004 Returning existing local MSP 2017-05-16 17 : 08 : 01.366 UTC [msp) GetDfaulS 工 g 且工 ngidet 工 ty -> DEBU 005 Obtaining d e fault sign 工 n g identity 2017 - 0 5 16 17 : 08 : 01.366 UTC [msp/ident i ty] Sign > DEBU 006 Sign : pla 工 ntex : OAB 1070A6708031AOC08F1E3ECC80510 . . 6D7963631AOAOA0571756572790 A0161 2017-0 5 -16 17: 08 : 01 . 367 UTC [msp /工 dent i ty] S i gn > DEBU 007 S 工 gn : d 工 gest : E6 1 DB3 7 F 4E8BOD32C9 FE10E3936BA9B8CD278 FAA1F3320B08712164248285C54 Query Result : 9 0 2017 - 05 -1 6 17 : 08 : 15 . 158 UTC [main) ma i 口-> INFO 008 Exit l 口 g . . . Query

= ==== == = = = ====== = ====

on

PEER3

o 口

chane

l

’ mychannel'

i s

successful

= ==== = =========== = === All GOOD , BYFN e x ecution completed ======== === ======= === |

-一

|\

I

| 你可以滚动这些日志来

L



_I



|- 一 一

I

一-

I I\



| 一 I



\ I I I I I I I I l_ l I \_I l 一



查看

各种









10

1 3 章

Fabric 叩m

J es

项目



析与实践

•!

347

. 如何查看链码曰志?



查 每个独



器的日志组合

的链码服



容器

来查看每

个容



内分隔的







下面 是每 个



码服

务容



$ docker logs dev-peer0 . org2 . example . com mycc 1 0 04 : 30 : 45 . 947 [BCCSP_FACTORY] DEBU : 工 nitalze BCCSP [SW] ex02 Init Aval = 100 , Bval = 200 $ docker l ogs dev-peerO.orgl . example . com- mycc-1 . 0 04 : 31 : 10 . 569 [BCCSP_FACTORY] DEBU : In 工 tialze BCCSP [SW] e x02

工 nvoke

Query Response {"Name ”.” a ” , ” Amount …” 100 ” } ex02 Invoke Aval = 90 , Bval = 210 $docker logs dev-pr l .org2ex 出 nple . com mycc 1 . 0 04 : 31 30 420 [BCCSP_FACTORY] DEBU : Initialize BCCSP [SW] ex02

工 nvoke

Query Response : { ” Name ” : " a ” ,” Amount ” :” 90 ” }

了解

13.2.10 B YFN

Docke



r Compse

例给我们



技术





两种风格的

Docker

c omp os e bas e . yaml ( base 给我









1 个

CLI

页面上的所有说明

容器







剩余部分

1 个



细信息,请 第







种风格



端的测试



织的

CA

o rd





除了



) 。

第一

er

种风格一



容器和

4 个

件,



p e

docker



都继承



- compse

r 容器



docker

- cl

我们用





i . yaml

件来展开这个





perOgan

io

n s/ o

例 rgl



样的私钥











样需要编辑

的 们

需要



m ple







使





No

容器

doc

指出



e.

组织

CA 的私

d e.



/ 。

. com/a



S D K







来运行端



能够向组

, Org2



- compse



- e 2e 你可以在

c r yp

们将使用 的

. :,咄

路径为

nl







ca

cal

serv

更新



FABRI

crypto

- co crypto

- co n

路径,并

C _CAS

nf

ig/ f i g/

E RVE_







,

t o -c o n fig

/。

caO

n d 去

k er 的私钥



com/a

里为

coma



况下想使用 Orgl

. yaml

有关运行这些测试的详



,为了定位

. exa

- e2

变量

脚本 我

,被构

册和 登 记

. exampl

a tions/rg2

docker-mps

2 次

. sh



fa b r i c - ca 注

byfn 个

. y a ml

它还运行

求以用于



at

a niz

KEY FILE

s e - e2

4 个轻微的修改 iz

0 rg



心 叫。以件



, 的

有运

d o 阳

仓库

的功能之外

。举



S D K

R EST

要进

设计的

- comp

文件夹中找到这些值

er

SDK

Node

S D K

果你在没 需

盖了为

docker

节点发送



供了









p

目录

Compse

CA

T LS

容器提

仅供非

•!•

348



使用



Couch

数据库

是,



默 提



认的

golevdb

了额

CouchDB



c ompse-cuh

切换到

外的能力

替默

来根据 认的

. yaml

CouchDB

JSON



形式





链码同样能使用

数据

库(

之外,请

golevdb



chaincode _ exampl02

现 bri

思、

性的影响



CouchDB Web

限制对

),除

循前面提到的



码服

了 生



应该使用下面的

务数据提



成配





启动网络的时 置

CouchDB

couh

在开发环

db

候 文件的过







丰富、复杂

传递

docker­



境 界面

CouchDB



中映射端口可以使

CouchDB

( Fauxton

)对





容器的外部访问

fabric

询,你将

/ exampls



建 . orgl

目录中找到

.exam pl

巳 . com



marbles02

可用,并九许通过

l es02

链码交互 上安装和



_ exampl02

据(例如

和加入信道

,然而为了执

marbles02

链码服务

建 marb

perO

识到了安全

生产环境将避免端口映射,以

chainode

JSON

和加入频道的过程创

信道,请使用以下步骤与 在

来执行

使用被格式化为 / go

我们将按照上述创

docker



需要

/ chainode

REST API

据库进行可视化

CouchDB

CouchDB

-f



容器端口映射到主机,请确保你

你可以使用上面列出的步骤通过





C ouchDB

CHANNEL_NAME=$CHANNEL_NAME TIMEOUT= docker-compose compse-l 工 . yaml -f docker-compose couch.yaml up d

主如果你选择将臼

途或交



使用





DB

可以从

CouchDB





H yp e rl ed ge r Fabric j 原代码分析与深入解读

13.2.11 状态



)。

你可以



。一



旦你将

per

节点加入到了



。 例化链码:

# be sure to modify the $CHANNEL_ NAME variable accordingly for the instantiate command

peer

cha 工 ncode install -n marbles v 1 0 p g 工 thub . com/hyperldgfabi examples/chaincode/go/marbles02 peer cha 工 ncode insta 工 ate -o orderer . example . com : 7050 --tls $CORE_ PEER_ TLS_ ENABLED - -ca f i 1 e /op t/gopa t h/s rc/g it hub . com/hyper 1 edger /fabric/peer I crypto/ordererOrganizations/example . com/orderers/orderer . example . com/msp/ tlscaer/ . ex 缸 nple . com-ert . pem C $CHANNEL_ NAME n marbles -v 1.0 -c ’ { ” Ar gs ” : [ " init " J l ’ - P ” OR ( ’ OrgOMSP . member p ,’ OrglMSP . member ' ) ’t

创建





marbles



移动它们:

# be sure to modify the $CHANNEL_NAME variable accordingly

peer chainco d e 工口 voke -o orderer . example com : 7050 --tls $CORE_ PEER_ TL S 一 ENABLED - caf ile /opt/gopath/src/gi thub . com/hyper ledger I fabric/peer /crypto/ ordererOrganizations/example . com/orderers/orderer . example . com/msp/tlscacerts/ tlsca . example . com-cert . pem -C $CHANNEL_NAME -n marbles -c ’ {” Ar gs ” : [ ” initMarbl 巴 ” , ” marblel ", ” blue ”, “ 35 " ,” t om ”])’ peer cha l 口 c ode l 口 voke -o orderer . example . com : 7050 --tls $CORE_PEER TL S 一

学习使



ENABLED - cafile

主政

349

. com/hyperldgfabit

工 thub /optgahsrc



. exampl

. com/rdes ordeOganizts/xmpl

•!

l e s 项目分析与实践

c - s amp Fabri

章 13



mi 业

i '阳 仅供

tlsca . example com-cert . pem -c $CHANNEL NAME

. com/sptlaer

-c ' marbles

-口

[ " initMarbl ’':

{ ’『 Args

e tls $CORE_ PEER_ TLS_ -o orderer . e x ample com : 7050 L nvoke peer ch a 工 n co d 巴 工 c /percyto ENABLED - - caf ile /opt /gopath/src/gi thub . com/hyper ledger I fabr ordererOrganizations/example . com/orderers/orderer example . com/msp/tlscacerts/ tlsca . example . com-cert . pem -C $CHANNEL_ NAME -n marbles -c ' { ” Arg s ": [ ” initMarbl e " , ” marble3 ”,” blue ”,” 70 ” ,” tom " J l ’ o orderer . example com : 7050 --tls $CORE_ PEER_ TL S 一 peer chainco d e invoke 工 C /peer/crypto/ . com/hyperldgfab ENABLED - cafile /optgahsrciub ordererOrga nizations/example . com/orderers/orderer . example com/ms p/tlscacerts/ -c ’ ( "Arg s ” : [ " transferM marbles tlsca . example . com-cert . pem -C $CHANNEL_ NAME -口 arble ”," marble2 " ,” jerry " )} ' peer c h a l 口 c ode invoke -o orderer . example . com : 7050 --tls $CO R E_ PEER_TLS_ ENABLED - - caf ile /opt/gopath/src/gi thub . com/hyper ledger I fabri c /peer /crypto/ ordererOrganizations/example . com/orderers/orderer . example . com/ms p/tlscacerts/ tlsca example . com-cert . pem -C $CHANNEL_ NA!1E -n marbles -c ' ( "Args " : [ ” transEerM 『 ” ]} arblesBasedOnColor ”, ” blue ” , “ jery peer chaincode invoke -o orderer example . com : 7050 --tls $CORE_ PEER_ TLS hub . com/hyper 1 edger/£ abr i c/peer/ 工t ENABLED - - cafile /optgahsrc crypto/ordererOrgan i za t ions/example com/orderers/orderer . example com/ n marbles -c msp/tlscac e rts/tlsca . example . com cert . pem -C $CHANNEL_ NAME ’ { ” Args " [ ” delete

)的数据库以及它文 字

名 信道

的 一

(或者你的唯 mychanel

个名为 一

到 看

你应该可以



: http :// localhost:5984/ _ utils URL

) 通过在浏览器导航中打开如下 Fauxton

界面(

CouchDB Web

通过 端口,那么你现在就可以

C ouchDB 文件中映射你的

docker-mps 如果你选择在

: 档在里面



Note For the below commands , be sure to update the SCHANNEL_NAME variable appropriately

查 中运行常规的

CLI 你可以在

peer chaincode

Query Result : ( ” color …” red ” ,” size ” : 50)

的历史记 marble

你可以检索特定

输出在

):

-c ’ { ” Ar gs

r 自 rbles

”:

” ])

b le2

a ’ mar

e ”,

[ ” readMbl



的输出如下: marble2

peer

e2 marbl

词(例如读取

-c $CHANNEL_ NAME -n

q 」 ery

。 变量被更新了

$CHANEL_M 主对于下面的命令,请确定



marble ”:

, ” docType

,例如

marble

",

name

le



交易



OW



j ”:

er

1:

query -c $CHANNEL NAME - n marbl es - c e ncod c h a工 l e ”,” marb lel " J } ’

marb

”,

” · ” marble2

' ( "Args ": [”

g e tH

工 s to

ryF

orMab

erry "



流学习使用

仅供非商业用途或交流学习使

350 令

Hyp

e 出

dg

e r Fabric

源代码分析与深入解读

Query Result :[{” Tx

":” lc3d3caf124c89f91a4c0f353723ac736c58155325f02890adebaal5

工 d

: { ” docType ":” marble ”, 口 arne ”: 叮 narble ’B ,” colr 『气’ t blue ”,” s iz e ": 35 , ” OW 口 er :“ t om ” }} , { ” Txid :“ 755d55c281889eaeebf405586f9e25d71d36eb3d35420 af833a20a2f53a3eefd ” , ” Value ”: { ” docType ":” marble ”," name ” :” mar bl el ”,” color ”:” blue ”, size ’门 35 ,” owner '『:” 〕 ery ” } ,{” Txid " : " 81945032de67fa 5 5e04 f14 7 8 8ee3 3e2 8b23 2 eef3 6d9 8 f ’t , ” Value ” }] e164

”,’'

Value



M

H

你还可以对数据内容执行丰富的查询操作,例如通过拥有者

jery

peer chaincode query -C er ”,” j erry ”]} ’

输出应该显示

2 个属于

$CHANEL

_NAM

jery



marb

E



查询

marble

:

-c ’ { ” Args ”: [” queryMarblesByOwn marbles

l e:

Query Result: [ { "Key ":” rnarble2 ” , ” Record ": { ” color ":” red ”,” doc Type ’ 1 :” marble ”, ” 口 am e ”: M rnarble2 ” , ” owner ”·” j erry ”,” size ”: 50}}. { ” Key ”:” rnarble3 ” , ” Record ": { ” color " :” blue ”,” docType " :” marble ", " name ” :” rnable3 ”, OW 口 er": " jerry ”," s ize ”: 70}} l

关于数据持久化

13.2.12



如果需要在

per

容器或者





CouchDB

容器进行 数据持久

化,

器内相应的目录挂载到容所在宿主机中

。 docker-mpsba

. yaml



件下

per



种选择是将

Docker



例如,你可以添加下面的代码到

的约定中:

volume s: - /var/hyperledger/peerO:/var/hyperledger/production

对于

CouchDB

容器,你可以在

CouchDB

的约定中添加如下两行代码:

volumes · /var/hyperledger / couchdbO : /opt/couchdb / data

故障

13.2.13





始终保持你的网络是全新



contaiers





chainode

使用以下命

令来移除

之前生成的

a r tifacs,

crypto,

images:

. /byfn . sh -rn down

如果你不移除容器和

镜像,

如果你看到 后重启你的

Docker Docker

你将

相 进程

关的错误信



内加密材料导致的错误

会看

到错误信息

息,则应检

Docker

的问题通常不



删除你的

镜像,并



头开始



docker rm f $(docker ps -aq) docker rrni f $(docker images -q)

如果你发现的创建、





例化、调用或者查询命令,请确保你已经更新了信道和链码

会被立



你的版本(应为 即识别

1.2 。

例如,你可能会看到由容器

或更



版本),然

仅供



的名字



提供的示例命令中有占位符

1 3 章

Fabri

「阳

nples

项目分析与实践



3 51



如果你看到下错误,则说明可能以前运行的链码服务(例 com

' I 二商业用途或交流学习使

dev-prl

- myc-1.0



de

v-perO

.org

l .e

xampl

e.

com

- myc

- 1 . 0 )有

.org2.example.

问题



Error: Error e 口 do rs ing chaincode : rpc er ror : code = 2 desc = chaincode code my cc : 1. 0 (cha 工 ncode /var /hyperl dger /producti mycc.1 0 exits)

删除这些链码服务,然后重试

Ero

工 nstalig 口/

chainodes/



docker rm

工-

$(docker images I grep peer[0-9] peer[O 9] f

如果你看到以下错误信息,则应确保的

Fabric

1. 0 . 0-rcl

镜像上

I awk

$3 ) ’)

『 ( print

网络运行在被标记为

lat

est





Error connecting : rpc error : code = 14 desc = grpc : RPC failed fast due to transport fa

工 lure

Error : rpc error

code = 14 desc = grpc: RPC fa

fast due to transport failure

工 led

如果你看到了以下错误内容,那么说明没有正确设



configtxe

工具需

CFG_PATH=$WD

要这个变量才能找到

configtx.

,然后重新创建

chanel

配置

工 g tx/tool/localconfig] Unsupported Config Type '『” panic : Error reading conf 工 guration

Load

[conf

要清理网络,请使用

down

)咄

选项

:

nl



FABRIC_G

_P

返回并执行

A TH

环境变

export

量。 FABRIC_

。 ->

CRI T 002

Un supported

Error reading

Configτ'ype

co



f 工 guratio



” t’



. /byfn . sh -m down





你看到

一条指示中依然有“

active

endpoits

”,然后

清理 你的

Docker

网络



会清除你之前的网络并且给一个全新环境: docker network prune

你将看到以下消息: WARNING! This will remove all networks 口 ot Are you sure you want to continue? [y/N]

选择

used by at least one container.

y 。

如果你仍旧看到了错误,请在

Hyperldg

享你的日志

Rocket Chat



13.3 注意,此基本配

basic-network 置

使用预先生成的证书



密钥材料,并且还具有预定义的操作来初始

的#

fabric-queston

频道上分

这将



仅供

•!•



352



H y p e rl ed ge r Fabric i 原代码分析与深入解

个名为“

mychan

i卖

n e l ”的通道



要重新生成这个材料,只需运行

ge 、

' I 二商业用途或交流学习使

口 erat

. sh





应该看

到以下输出:

bash

. /generate sh orgl example . com 2018-04-06 20 : 30 : 19 . 886 CST

[common/tools/configtxgen]

main -> INFO 001 Loading

2018-04 06 20 : 30 : 19 . 893 CST [comn/tlsf 工 gtxen] Generating genesis block 2018-04 - 06 20 : 30 : 19 . 893 CST [comn/tlsf 工 gtxen] Writing genesis block 2018 04 06 20 30 19 916 CST [common/tools/configtxgen] configuration 2018-04 06 20 : 30 : 19 . 920 CST [comn/tlsf 工 gtxen] INFO 002 Generating new channel conf igtx 2018-04 06 20 : 30:19.941 CST [common/tools/configtxgen] INFO 003 Wr 工 t l 口 g 口 ew channel tx 2018-04-06 20 : 30 : 19 . 971 CST [common/tools/configtxgen]

doOutputBlock -> INFO 002

configurat

工 0 口

configurat

网络,运行 、





main - > INFO 001 Loading doOutputChannelCreateTx -> doOutputChannelCreateTx -> main -〉工

NFO

001 Loading



2018-04 - 06 20 : 30 : 19 . 978 CST [common/tools/configtxgen] -> INFO 002 Generating anchor peer update 2018-04-06 20 : 30 : 19 . 978 CST [common/tools/configtxgen] > INFO 003 Wr 工 t ing anchor peer update

要启动

doOutputBlock -> INFO 003

star

. sh



你应该看到以下输出

doOutputAnchorPeersUpdate doOutputAnchorPeersUpdate



bash

. /start sh docker compose f dock er compose . yml up peerO . orgl example com couchdb Creating couchdb ... done Creat 工 ng peerO . orgl . example . com . . . done Creating orderer . example . com Creating ca . example . com Creating peerO . orgl . example . com # wait for Hyperledger Fabr 工 C to start

d

ca . example . com orderer . example . com

# Create the channel docker exec e "CORE_ PEER_ LOCALMSPID=OrglMSP ” e ” CORE_PEER_ MSPCONFIGPATH = /etc/ hyperledger/msp/users/Admin@orgl . example.com/msp ” peerO . orgl .example .com peer channel create -o orderer . example . com : 7050 -c mychannel -f /etc/hyperledger/ configtx/channel . tx 2018 04 06 12 : 33 : 25 . 408 UTC [channelCmd] InitCmdFactory > INFO 001 Endorser and orderer con 口 ect 工 0 口 S initialized 2018-04 06 12 : 33:25 . 457 UTC [channelCmd] In 工 tCmdFacory - > INFO 002 Endorser and orderer coneti 且 s intal 工 zed 2018 - 04-06 12 : 33 : 25 . 663 UTC [main] main-> INFO 003 Ex iti ng .... . # Join peerO orgl . example . com to the channel

仅供非商业用途或交流学习使



13



Fabr

i c-sa

mp

l es

项目分析与实践

·:

353

docker exec e ” CORE PEER LOCALMSPID=OrglMS P ” - e ” CORE PEER_ MSPCONFIGPATH=/etc/ hyperledger/msp/users/Admin@orgl example . com/msp ” peerO . orgl . example.com peer channel join 四 b mychannel . block 2018 04 06 12 33 : 25 867 UTC [chan 口 elCmd] InitCmdFactory -> INFO 001 Endorser and orderer conetis 工 nitalzed 2018-04-06 12: 33: 25 . 956 UTC [channelCmd] executeJoin > INFO 002 Successfully submitted proposal to joi 口 chanel 2018-04-06 12 : 33 : 25 956 UTC [mai 口 l main >工 NFO 003 Exit:ing ...

要停

止它,运行 、

stop

. sh

:

bash

. /stop . sh # Shut;: down the Docker co 口 tai 口 ers that m 工 ght docker-compose -f docker compose . yml stop Stopping peerO . orgl . example.com . . . done Stopping couchdb . . done Stopping orderer example . com . . . done

运行

teardown 、

. sh



在你



系统上彻底

be currently

删除网

络的所有“



run

罪证据”

工 ng



bash

. /teardown . sh Removing peerO . orgl . example com . Removing ca . example.com . Removing couchdb . . Removing orderer.example . com . .. Removing network net_basic

b asic

-network



first-n

D basic-network 个节点

,因

网络,旨在 密码材料



基本应用程序所需的最 日 rst-newok

的部 署 同的,因为

拓扑 first-newok

实际上动态

Fabcar

13.4.1 编写第一个应用

基于

Hyperldg



立了一个拥有多

量。

它只有 个组织的

。 。

指引你

小节点数

只有一个单的组织。

包括预先生成的密码材料

本节将会

最基本的区块链网络应用程序需要提供给户查询账(包含特定记录)以及更新

此也

是不



chainode

演示更真实

work

13.4

的对比

建立了开发

一 D

etwork

done done done done

Fabric

网络 编写第一个应用程序。



成新的密码材料,而

basic

” net­

仅供非商业用途或交流学习使

354



Hyp

e rl e dg

e r Fabric

(添加记录)的功能

源代码分析与深入解读

。 我们的应用程序基于

Javscri

里将通过



pt

,通过

Node.

SDK

步来指导你编写第一个应用程序







( 1 )启动一个

Hyperl

的组件来

与(账本所在的)网络进行交互



dger

询和更新账本

Fabric





这些组件有

器则用来发送一些管理命令

per



( 2 )学

区块链测试网络 节点、

在我们的网络中,需要一些最基本

ordeing

节点以及证书管理

有个简单的脚本将下载并启动这测试网络 参数

以让我们可用





CLI





习应用程序中到的智能合约例子所

详尽的数据



种方式和账本进行交互





智能合约包含的各种功,

比如,我们可以读取整体的数据或者某一部分



( 3 )开发能够查询及更新记录的应用程序



账本,另

一 这些功能

个用于更新账本



我们的程序将使用



们提供两个程

序例子

SDK

APis



一个用于查询

来和网络进行交互,并最终调用

。 完成本例之后,你应该基了解一个使用

Hyperldg

能合约的应用程序,是如何与

Hyperldg

Fabric

下面,让我们启动测试网络

Fabric Node.js SDK 网络中的账本进行

交互



并带有智





下载测试网络(

13.4.2 访问

Geting

Prequist

选择

a Test Network)

网页井确保你的计算机上已经安装了必需依赖项

clone

fabric



- sample

的工作目录,运行

clone

git clone htps : /github . cd fabric-samples/fabcar fabcr

com/hyperldgfab



令,并

进入

工 c-sample

子目录包含运行示例程序的脚本及代码

fabcr

子目录:

. git



在该目录运行

Is



令,你应该会

看到以下内容:

chaincode 现在调用

st

invoke.js

network

artFbic

. sh

下面命令将会载并解压

package . json

来启动网络

Fabr

j

Fabric Docker images



.sh

工 c



下面

是关

说明: 启动

per

节点



Ordeing

口创建一个通道,并将



CLI

容器



。 per

例化会启动链码容器

证书颁发机构及

加入该通道

将智能合约(即链码)安装到 实

口调用

节点

per 0

节点的文件系统上,并在通道实例化该链码;

。 intLedgr

startFabric.sh

,因此需要几分钟时间来完成

为了简洁起见,我们不会深入解这个命令的具体细节 0

s





Hyperldg . /star

que

功能来向通道账本写人

仅供非商业用途或交流学习使

10

辆不同的汽车



于这个命令的简要

仅供非商业用途或交流学习使





主这些操作通常由组

织或者 命令,但

SDK

docker

ps

Your First Network 图



Fabri

管理员来完成

c -sar

。 。



J es 项目分析与实践

·:

这个脚本使用

具体请参阅

CLI~

Hyperldg

355

器来执行这些

Fabric Node SDK rep





发送



r 的

中也有相应的支持

中的示例脚本



pe

13



令可

以显



starFbic

.s

h

脚本启动的进程



13-

部分了解有关这些操作的详细信息和机制



所示简单描述了





应用程序与

Hyperldg

r Fa

你可以



Buildn

g

bric

在这里,我们专注于应用程 网

络交互的过程



区块链网络 应用开发者身份

日 图

应用程序使

AP

过名称和版本进行标识

Is

来调用

智能合约(

。 ,其名

API





dev

fabcr

,版本号是

,除此以外



( SDK

有资产),如图



运行

per



Fabric



这些智能合同托

管在

dev-prO

) 进行访问



提供了



络中

, 并通

Jav

. orgl.exampc

在本节中,我们将使用



SDK

Hyp 和

CLI

用于开

发应用程序

巳 rledg 。

查询账本

查询是指如何从账本中读取数据



JSON

链码”)

peerO . orgl . example . com - fabcar-1.0

1 0 ,

工具

Fabric Node SDK

似于

络交互过程

即“

例如,标识为

可通过软件开发

13.4.4



应用程序如何与网络进行交亘

13.4.3

的容器

13-



的数据 13

存储格式写人的, - 2 所示

你可以查询单个或者多键的值,如果账本是

则可以执



行更复杂的搜索(如查找包含某些关键字所



己飞 仅供书商业用途或交流学习使



13-2



询过程

仅供非商业

•!•

356

Hype



ger

F 由





我们还有

途或交流学习使



IC 源代码分析与深入解读

正如前面所说,我们的示例网络有

的账本



一些

Javscript

个活跃的链码容器和



示例代码,我们可以使用

账本中关于车的详细信息

个已经包含

10

fa bear



录中的

辆不同汽车

query.

js

来查



。 在我们查看该应用程序的



作原理之前,



要安装

SDK

模块





f abcr





中,运

行下面的命令: npm

l 口

stal

后续命令也都是在

fa bear

现在我们可以运行 表



录中运行的





JavScript

程序

应用程序中预先加载了一个



query

首先,运行

query

All Cars

扣程序,返回账本上所有汽车列

函数,用于查询所有车辆因此我们可以简

单地运行程序: node que

js 可.

返回的信息如下: Query result count = Response is [ { ” Key ” Prius

”,

owner

":”

1 : ” CAR

。 ”,

Tomk

”: { ” colour ”:” blue ”,” make ”:” Toyota ”,” model ”:” Record

。 ” }



{ ” Key ”: "CARl ” , “ Record ": { ” colour ”:” red ”,” make " : ” Ford ”, ” model ”:” Mustang ”, ” owner ”·” Brad " }} , { ” Key ”… CAR2 ” , ” Record ": { ” colour H :” green ”,” make ”:’『 Hyundai ”, model ”: Tucson ”, own er ”·” Jin S 。 ” } , { ” Key ”:” CAR3 " , ” Record ’· . { ” colour ”:” yellow ",” make ”.” Volkswagen ”,” model ":” Pas sat ”, ” owner ":” Max ” }} , {” Key ”·” CAR4 ” , ” Record ": { ” colour ” : ” black " ,” make ” :” Tesla ”,” model ” :” S ”, owner ":'『 Ad ria 口

a

" },

{ ” Key ”.” CARS ” , ” Record ”. { ” colour ”:” purple ”,” make ”:” Peugeot ”,” model ”:” 205 ”,” owner :” Michel ” }} , { ”

Key

”.

CAR6

”,

Record

":

{ ” colur

”:

wh

工 te

”,“

make

”:

Chery

",”

model

” :”

S2L

”,

ow

口 er

":

” Aarav " )) , { ” Key ”:” CAR7 ” , ” Record" : { ,。 colur ”:” violet ”,” make ” : ” F 工 at ’e ,” model ”:” Pun to ",” owner ’ :” Pari ” }} , { ” Key ”:” CARS ” , " Record ··: { ” colour ”: 工 nd 工 go ”,” make ”:” Tata ",” model ”: Nan 。 ”, owner "· ” Valeria " )) , { ” Key ”:” CAR9 ” , ” Record ”: { " colour :” brown ",” make ”:“ Holden "," model ”: ” Bar 工 na ”, owne

t

N

r

这里有

”:

Shotar

10

Mustang

、一

键字是从

” }]

辆车, 辆

CARO



一 属于



Pari

辆属于

Adrian

CAR9

的紫罗

现在让我们来看代码内容





的黑色 色

Fiat

这一点特别重要

Tesla Punto





使用编辑器(例如

账本是基于

Key

atom 、

path.jo



辆属于

Brad /飞 falue

的红色

Ford

的,在这里关



应用程序的初始部分定义了变量,如链码 var options = { wallet_path :

Model i 一

工 n

( 一_

仅供#商业用途或交流学习使

a 工 rname

,’



visual

通道名称和网络端点:

studio

. / networ

k

/creds

『),

)打开

que

可 .js

程序





13



Fabric-smple

项目分析与实践



357

user id · ’ PeerAdrnin ’, channel_id : ' mychannel ’ , chainode_

:『

fabcr

’,

network_ url : ' grpc : I /localhost : 7051 ',







建查 询的代码块



II queryCar - requires 1 argument , ex : args : [ ’ CAR4 ' ], II queryAllCars requ 工 res no arguments , ex : args : [ ' ' ],

= {

canst request

: optins . txid : transaction_id, fen: ' queryAllCars ' , args : [' ' ] chainode

工 d

我们将

chainode_

chainode



用该链码中定义的

一工



赋值为

, d

fa bear

queryAlCas

,这让我们

定位到这个特

的链码,

然后调

函数。

在前面,我们发出

node

我们能够使用的唯一功

query.js



令时,会调用特定函数来查询账本。但是这不

。 要查看其 到





intLedgr

容 ,可转 、

们仔细



inc ode

,、

A II Cars

(s

cha

queryCa

query

func



query

子目录并在编辑器中打开

All Cars

fabcr.go 、

creatC



函数是如何与账本进行交互的



changeCrOw

等函数

你会 。

看 让我



会 SmartCon

queryAllCars(APistub ct)

shim

. Cha

工 ncodeStubirfa)

sc . Response { startKey :=” CAR 。” endKey : = ” CAR999 " resultsiterator , err

该函数调用

shim

间的账本

:=

接口函数

APistub.GetStateByRange(startKey, endKey)

GetSaByRng

来返回参数在

数据。这两个键值分别定义为

辆汽车(假设

CARO



Keys

1 3-

所示

演示了一

都被正确使用),



CAR9

queryAlCas

。因

star

Key

此,我们理论上可以创建



10

函数将会显示出每一辆汽车的信息

个应用程序如何在链码中调不同功能



。 /一下\

\/ createCar queryAll Cars queryCarProerties updateCarColor updateCarOwner



智能合约

13

- 3

仅供非商业用途或交流学习使

应用程序在链码中的功能



endKy

有 ~

358



H y per!



er

我们可以看到

F a bric

query

源代码分析与深入解读

账本,并



All Cars

终在链上增加



现在我们返回

query.js

将函数

query

们使用



creatC

个新区块



,这个函数可以让我们更新

下面先来了解另外一个查询



程序井编辑请求构造函数以查询特定的车辆

All Cars

CAR4

函数,还有一个称为



更改为

queryCa

,并将特定的“

所以我们编辑后的

Key

q uery.js

canst request = { chaincodeid : opti ns.cha txid . transaction_i d, fen : ' queryCar ’ , args : [’ CAR4 ’] 保存程序并返回

fa bear

程序现

i nc

目录



ode

一工

”传递给

args

参数

在应该包含以下内容 d

为达此目的,我们 。

在这里,我



,

现在再次运行程序:

query . ] S

口 ode

你应该看到以下内容: {’' colour ”:” black ”,” make ”:” Tesla

这样,我们就从查询所有车变成了只一辆



queryCa

函数,我们可以查询任意关键字(例如

Adrian

CARO

型号、颜色和所有者



黑色

Tesla

Mode l S 。

使用

),并获得与该车相对应的制造厂商、

。 现在你应该比较熟悉链码的基本查询功能以及带参数了,下面是时候更

新账本了·

. .

更新账本

13.4.5 我们已





完成了几个分类账查询井添加一些代码,现在更新本



新动作,首先让我们为手







辆新车



账本更新是从生成交易提案的应用程序开始



请求,用来识别要进行交易的通道

ID

S en

dTransctioPplA

该 把

求通过调用 交

易打包进区块,然后将发送到通道上的所有

endors



per



per(s

务的结

. reg

chanel

)进行认证

.



口 e l.



sendTransaction API

发送到

排序服务器





序服务器将

进行认证(在例子中,只有一个

) 。

isterTxEvn 果(

该程序然后调用

)返回一个提案答复,应用程序以此来创建和签署交易请求

eh

eh 机制

函数以及智能合约

per

最后,应用程序使 事

ing chan

endorsing per 用



就像查询一样,我们将会构造个

工将交易建议发送给 网络(即

我们可以做很多

. setPeerAddr 注册与特定交易

即成功提交或不)

ID





仅供#商业用途或交流学习使



API

API

相关联的事务

连接



per

获取事物提交结果,可以当作一个通知



的 该

事务 A PI

使应用程序能获得

监昕端口,

并调



。 主这里

我 们不

深 入讨论

交易流程文档





是简单

于这些

节 。

有关





Fabri c- sa mpl es 项目

易如何 最终提

交 给



•!• 析与实践

账本的详 细

359

信息,请 参





我们初始调用的目标 建



13

交 易的 JavScript

地创 建一

程序

个新的资

invok



巳. 。

就像 查

( 这里 询



一样





车) 。

我们有 一

个独 立

的用

使用编辑器打开程序并转到构

调用的代码块: var request = { targets : targets, chaincodeid : options . chaincode_id, fen . ’, args : [ ” ], chainid : options . channel_id , txid : tx id

我们可以调用函 Volt





数 并 把它归

CAR



IO 。



creatC

Nick

或者 。 在 账 本

cbangeCrOw 。

中我们的 Key

值已



存并 运行

出此

程序

事务

们回到

.

我们



来 调用 。

输出 一些提案



序并调用带有 CAR

,’

建一

用到了 CAR9

N 工 ck

响应和

应用程序通

Response is

Bary

,终端将会

通知,我们的 query

我们创

个红色的 Chevy

,所以这 里

我们将使

更新代码块如下:

var request = { targets : targets, cha 工 ncodei : options . chaincode_id, fen : ’ createCar ’ , args : [ ’ CARlO ’ , ’ Chevy ’ , ’ Volt ' , ' Red · c hainid: opt 工 ons.chael _ id , txi d: tx_ id





首先

交易

eh.rgistT

IO 参数

’],

ID 。

x EventAPI 的



queryCa

接收 函数

是, 我们

该通知

关 心的 是

, 将会看





现在 ,如

果我

到:

{ ’ colour ":” Red ”,” make ”: "Chevy ”, ”model ": "Volt ”," owner ” :” Nick ” } t

最后

一个

所以,我们 简单编辑

函数 changeCrOw invok

。 巳 .

Nick 很慷慨

,他想把的 Chevy

Volt

如下:

送给

var request = { tar gets : targets , chaincodeid: options . chaincode_id , fen : ’ changeCarOwner ’ , args: [ ’ CARlO ’ , ’ Barry ’], chainid: options . channel_ id , txid: tx id

询,将

再 次运行

node

会看 到如下结 Response is

p er

invoke.js 果

, 之 后再运行查

询 程



{ "colour ”" Red ” ," make ”:” Chevy

仅供非商业用途或交流学习使

序,我们仍 然是使 用

CAR IO

作为 参数查

•!•

360

13.5

Hyperledger Fabri

c 源代码分析与深

入解读

Balance transfer

Balance transfe

是一个

Node.j s SDK API

Node

扣应用程序

示例

- client



fabric-

- client



0 Docker - v 1.12 or higher, Docker 口

Docker

Com

p os

版本

1.2

Node. docker

镜像

版本

,有克隆

j s v8.4.0 or higher, Node.js 下载

或者更高的版本;

巳 - vl.8 or higher, Docker Compse

0 Git cl ient - needed for clone comands 0

fabric

预置环境

13.S.1



,用来演示

命令的

1.8

版本在

8.40

Git



或者 更高 的版本



户端;

或者更高的版本;



cd fabric-samples/balance transfer/ 完成上述设置之后,你将使用以下

Docker 口

2

容器配置来本地网络:

CAs;

0 A SOLO orderer; 4 口

peers (2 peers per



) 。

工件

13.S.2 使用

Org

Hyper

l e d ger

节点,即

Fabric

ordeing

中的

cryptogen

节点和

工具生

CA

容器



成加密材料,并将其安

关于



cryptogen

工具的更

hyper ledger-fabric.readthedocs.io/ en/Iatest/build_network.html#crypo



一个

orde

创世区块

Hyperledger Fabric

( gensi.block

工具

中的

)和通道配

configtxe

的更多细节可以在

工具 htp

ht m l #config

ur

at i o n-

tra

n saction

处找到

b a l an golan

终端窗口



no

d e.

编写的

dock

)已使用

置 在工件文夹中

。 关于

configtxe



ce

- transfe



chainode

运行

例,对于这些选择中的

每一 。

巳 r - compse

启动网络



docker-compose -f artifacts/docker-compose .yaml up 2 :安装



mychanel.tx

1

l : 使用

终端窗口

处找到

/

://hyperledger-fabric.readthedocs. io/ en/latest/bui Id_network.

” genrato

有两种选择可用于运行

方法

生 成并放

htp:

运行示例程序

13.S.3 使用以

预先

细节可以在

- genrato

置事务(

到所有对

fabric-lent

仅供



fabric-lent

· 1 二商业用

节点模块

i 主或交流学习使用



个,你可以选择



npm

1 3 章

F a bric-sampl

es

项目分析与实践

•!

361

工 nstal



PORT

4 0 0

上启动节点应用程序:

PORT=4000 node app 终端窗口 g i t hub

3 : 从示例 .co

m

方法

/ hy

AP

p e rl e d g 巳 r / fa

I 请求部分执行

R EST

b r i c ” sa

mpl

es

/ t re

/ mas

API [Sample REST APis Requests] (https://

t e r / b a l ance

- tra

n sfer#amp

l e - rest

- ap

i s - requ

巳 st

) 。

2

终端窗口

1 :

cd fabric-samples/balance-transfer . /runApp . sh 这会在本地计



然后,启动

PO

RT4

终端窗口

2

需 的网 络

0 :

stedolan.githu b. io/j q 在终端

机上启动所



上的节点应用程序





fabr

i c-

l ient



fabric

- ca

- c l ient

节点模块,



为了让下面的

she

l 脚本正确解析

JSON

,你必须

安装

jq



请参



htps:

/

i 。

l 启动应用程序后,通过执行脚本测试

API

一-

tesAPi

. sh:

cd fabric-samples/balance-transfer #要使用

golan

chainode

. /testAPis . sh #或者使用

node

. /testAPis sh

13.54

示例一-

,请执行以下命令

-1 . js

gola

口 g

chaincode 1 node

REST

下述请求描了

Ba

APls 请求 l ance

transfe

示例,其利用

REST

作及查询的接口,方便用户直通过访问

API

暴露了一

系列

Fabric

网络操



1.

登录请求

在组织中注册并新用户一



Orgl

:

curl -s -X POST http : //localhost:4000/users -H "content-type : application/x-wwwform-urlencoded " d ' username=Jim&orgName=Orgl ' 输出



” succes s ” : t rue, "secret ”:” RaxhMgevgJcm ”, "message ” : ” Jim enrolled Successfully ”, ” token ” :” ”

这是后

响应包 续请求

含 的请求头中必

了成功

/失败状态 需



一个

的字符串

e nrolmet

仅供



' I 二商业用途或交流学习使

Secrt



一个

JSON

Web Token (JWT),

362 令

2.

H y pe 出 dg

e r Fabric 源代码分析与深入解读

创建通道请求

向 Orde

节点发送创 建

通道的请求 。

curl -s -X POST \ http : //localhost : 4000/channels \ -H ” authorization : Bearer ” \ -H "content-type : application/json ” \ -d ’ { ” channelName ” ·” my channel ”, ” chanelCofigPt

头部



authoriz

3.

ion 必

须 包

含从



/ art

POST

工 facts/hnelry

tx "

I user 调用返回的

JWT 。

加入通道请求

将指定的区块链节点加入相应通道 。

curl -s - X POST \ http : //localhost 4000/channels/rnychannel/peers \ -H ” authoriz 工 on : Bearer ”\ -H ” content type : apl 工 cation/js " \ -d ’{ ” pers

4. 安装

”:

[ ” perO.ogl

. exarnpl.co

’ 『 ,” perl

. orgl

. exarnpl

. corn

'『 ]

chainode

将链码部署 到

区块链节点上 。

curl -s -X POST \ http : I /localhost : 4000 I chaincodes \ -H ” authoriz 口 : Bearer ” \ -H "content-type : application/json " \ -d ' {

’l peers ”:

[ " peerO . orgl example.corn ”,” peerl.orgl . example.corn ” ] , ” chaincodeNarne ” : ” my cc ”, “ cha 工口 code Path

” :”

” chainodeτ'yp

” chainodeVrs

。 主当

使用

ex : curl

. corn/exapl_g golan

no

chainode d e. js

。 ”,

”,

”.

node.js

必须设直为

github

”:

v 。 ”

时, chainode

chaino 的位直 。

削阶 也放在相同目录中

必 、须设直为节点,并且 chainodePt 。

s X POST \ http : I /localhost : 4000 /chaincodes \ -H " author 工 zatio 口 : Bear ” \ H ’ content-type: application/json ” \ l

-d ’ {

仅供非商业用途或交流学习使



13



Fabric-sampl es 项目分析与实践

•!

363

. exampl.corn ’『 ] , ” peers ”: [ ” peerO . orgl . exarnple com ” ,” peerl orgl ” chaincodeNarne ”:” my cc ”, ” cha incodePath ”:" $PWD/artifacts/src/github . corn/example_ cc/node ", ” chaincodeType '’ : ” node ”,

” cha

5.

实例化

工 ncodeVrsi

"

"

v 。 ”

chainode

对部署在节点上的链码进行实例





curl -s -X POST \ http //localhost : 4000/channels/mychannel/chaincodes \ -H "authorization: Bearer ” \ 工 cat 工 on/js ” \ -H ” content-type: apl d ’{ ’t peers ”: [ ” peerO . orgl . exarnple.com ”, N peerl . orgl . example.com " ], ” chaincodeName ” :” my cc ” , ” chainodeVrs

”.

v 。”,

” chaincodeType ” . ” golang ”, ” args

使用

nod

6.

巳.

”:

[ ’『 a

’ 『,”

10

chainode

",”

时 ·,

b

”,

20

。”]

chainod

巳 T :扩 p 巳必

、须设置为节点



调用请求

调用

Fabr

cur l

ic

网络中的链码方法



s -X POST \ http : //localhost 4000/channel s /mychannel/chaincodes/mycc \ -H "authorization : Bearer ” \ -H "con t ent-type : application /] son ” \



d ’{ ” peers ”: [ ” peerO . orgl . exarnple.com ”,” peerl orgl.example . corn " ] , ” fcn ” :” move ”, ” args ": [ ” a ”,’『 b ” , ” 10 ” ]

主确保存响应中的

事务 7. Chaincode



查询

调用链码中相应的查询方法 curl

队以便在随后的查询事务中传递叫树

。 s X GET \ ” http : //localhost : 40/chanelsry exarnpl

.corn

工 ncodes/my?pr

&fcn=querya

gs

=屯

5B

革 2a

屯 2

在 5D

"

\ -H ” author 工 zation : Bearer " \ -H ” content-type : application/json ”

仅供非商



用途

或交流学习使



=p

erO

. orgl

.

36

4



H yp

8.



e rl e d ge r Fabr



Block

通过

i c 源代码分析与深入解读

N umbe

Block:Num

r 查询区块

b er



询区块

信息。

curl -s -X GET \ http : I /localhost : 40/chan

且 els/mychanbok?pr=O.g

’'

. exampl

. com





” \ H ” content-type : application/json " " author

-H



9 . 通过

Tr

通过





Bear

查询 i onID

-s









具体交易信息



GET

http : //localhost : 4000/channels/mychannel/transac t id here>?peer=peerO . orgl . example.com \ -H ” authorization : Bearer ” \ -H "content - type application/json ” -X

transc



工 0 口

ansctiolD

Transct

curl

zat

事务

ID

可以来自任何先前的调用



l 794cbl 7e2164c3f9

10

. 查询





ons/