├── Flink ├── Flink使用场景.md ├── Flink分布式运行环境.md ├── Flink数据流编程模型_Window.md ├── Flink数据流编程模型_抽象等级.md ├── Flink数据流编程模型_程序和数据流.md ├── Flink是什么.md └── media │ ├── Leverage_In-Memory_Performance.png │ ├── checkpoints.svg │ ├── levels_of_abstraction.svg │ ├── parallel_dataflow.svg │ ├── processes.svg │ ├── program_dataflow.svg │ ├── state_partitioning.svg │ ├── tasks_chains.svg │ └── tasks_slots.svg ├── Hyperledger ├── Channel 配置 (configtx) .md ├── ConfigEnvelope.md ├── Hyperledger Composer介绍.md ├── Hyperledger Fabric 1.0架构介绍.md ├── Hyperledger Fabric MSP和Fabric CA的区别.md ├── Hyperledger Fabric 源码和镜像下载.md ├── Hyperledger Fabric中的 事件框架.md ├── Hyperledger基础介绍.md ├── Identity介绍.md ├── MSP图解.md ├── MSP详细介绍.md ├── Peer channel-based event services .md ├── Read-Write set语义实现细节.md ├── System Chaincode .md ├── chaincode源码解读.md ├── gRPC介绍.md ├── media │ ├── .png │ ├── 15210198930172 │ │ └── 15210201011823.jpg │ ├── MSPElemment.png │ ├── MSPLevel.png │ ├── MSPOrgRelation.png │ ├── MSPxmind.png │ ├── MSPxmind2.png │ ├── channelArtifacts.jpg │ ├── cryconfig01.png │ ├── crypto-config.png │ ├── dockerContainers.jpg │ ├── gPRC.png │ ├── localAndchannelMSPS.png │ └── transactionFlow.png ├── 使用go编写gPRC服务.md ├── 向一个Channel中添加Org.md ├── 开发Chaincode .md ├── 构建你的第一个区块链网络 .md └── 部署维护Chaincode.md ├── Readme.md └── Spark ├── SparkStreaming和Kafka的整合方式 .md ├── Spark入门三部曲之第一步Spark基础知识 .md ├── Spark入门三部曲之第三步Spark程序的开发和运行及WordCount.md └── Spark入门三部曲之第二步Spark开发环境搭建.md /Flink/Flink使用场景.md: -------------------------------------------------------------------------------- 1 | # Flink使用场景 2 | 3 | ## Flink的主要特点 4 | 5 | * 支持流式数据处理 6 | * 支持批数据处理 7 | * 复杂的状态管理 8 | * event-time 处理语义 9 | * exactly-once状态一致性保证 10 | 11 | ## 场景一:事件驱动程序(Event-driven) 12 | 13 | ### 什么是事件驱动程序? 14 | 15 | 事件驱动程序,是有状态的程序。从一个或者多个事件流中,接受事件,并且通过触发计算,和进来的事件进行交互,产生状态更新或外部操作。 16 | 17 | 事件驱动程序,是对计算和数据存储层分开,这一传统设计方式的提升。在传统架构中,程序从远程的事务性数据库中读取数据,并且向其中存储数据。 18 | 19 | ### Flink是怎样支持事件驱动程序的? 20 | 事件驱动程序性能,被流式处理器,处理时间和状态的能力限制。 21 | Flink的许多优秀的特点,都是围绕时间和状态的处理,展开的。 22 | 23 | * Flink提供了一系列的状态管理原语,可以管理超大量数据(太字节)的exactly-once一致性保证。 24 | * Flink支持event-time,高度自定义的窗口逻辑(window logic),细粒度的(fine-grained)时间控制。 25 | 26 | * Flink有一个包,能进行复杂事件处理(Complex Event Processing 、简称CEP),它检测数据流中的模式。 27 | * Flink的另一个优秀特点是savepoint。一个savepoint是一个连续的状态镜像(image),这个镜像可以用来作为兼容程序的开始点。给定一个savepoint, 应用程序可以更新或者调整它的规模, 或者一个程序的多个版本,可以用来做 A/B 测试。 28 | 29 | ## 场景二:数据分析程序 30 | 流式数据分析,比批处理数据分析的优点,批处理过程中的一个环节失败了,整个过程要重新计算。 31 | 流式数据分析,有失败恢复机制。 32 | 33 | ### Flink是怎么支持数据分析程序的? 34 | Flink有ANSI-兼容SQL接口,对于批式查询和流式查询,有相同的语义。 SQL查询,不管运行在静态的数据上,还是实时的事件流上,会计算出相同的结果。 支持丰富的用户自定义函数。Flink的DataStream API、 DataSet API 能提供更灵活的自定义,更低阶的控制。Flink的Gelly包,提供对大规模数据的图分析。 35 | 36 | ## 场景三:数据管道程序 37 | ETL(Extract-transform-load)是在存储系统间,转换和移动数据的常用方法。通常,ETL 任务是定时触发,从一个关系型数据库拷贝到分析型数据库,或者数据仓库。 38 | 数据管道程序和ETL任务目的类似,最大的特点是,实时处理。 39 | ### Flink是怎么支持数据管道的? 40 | 41 | Flink 提供了到各种存储系统的connectors,例如Kafka, Kinesis, Elasticsearch, JDBC。也类似Flume,有source和sink,source可以监控目录。 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Flink/Flink分布式运行环境.md: -------------------------------------------------------------------------------- 1 | ## Tasks和Operator Chains 2 | 对于分布式运行,Flink中,每个操作符就是一个子任务,Flink把这些子任务链接成一个任务。每个任务都被一个线程执行。把操作符链接成任务,是一个很有用的优化,可以减少线程到线程切换的开销,和缓存的开销,在减少延迟的时候,增大吞吐量。 3 | ![](media/tasks_chains.svg) 4 | ## Job Managers, Task Managers, Clients 5 | Flink运行环境中有两类进程: 6 | JobManagers(也叫做masters) 协调分布式执行。协调tasks、checkpoints, 当任务失败时,协调恢复。 7 | 集群中至少有一个Job Manager,高可用环境下有多个JobManagers,一个是leader, 其它的是standby. 8 | TaskManagers(也叫做workers) 执行数据流的tasks, 缓存和交换data streams。 9 | ![](media/processes.svg) 10 | ## Task Slots and Resources 11 | 每个worker(TaskManager)都是一个JVM进程, 可以在不同的线程中,执行一个或多个子任务(subtasks)。work有task slots概念,来控制一个worker可以接收多少个任务。 12 | 每个task slot代表TaskManager中一个固定的资源。有三个slots的TaskManager,会指定它管理的内存的1/3到每个slot。这样可以避免一个子任务和其它job的子任务竞争内存资源。注意,这里没有CPU隔离。 13 | 14 | 通过调整task slots的数量,用户可以决定子任务之间的隔离情况。每个TaskManager都有一个slot,意味着每个task group 都运行在一个独立的JVM中(例如,可以在一个单独的container中启动)。有多个slots,意味着,在同一个JVM中,有多个子任务。在同一个JVM中任务,共享TCP连接 (通过复用技术)和心跳消息。它们也可以共享数据集和数据结构,因此可以减少每个任务的开销。 15 | ![](media/tasks_slots.svg) 16 | 17 | 根据经验,task slots的默认值建议是CPU cores数目。 18 | ## state备份 19 | state备份方式的选择,决定了key/values存储的数据结构。一种state备份方式,把数据存储在内存Hashmap中,另一个备份方式,使用RocksDB。state备份,除了定义数据结构,还实现了,获取key/value state快照的逻辑,和存储快照作为checkpoint的一部分。 20 | ![](media/checkpoints.svg) 21 | ## Savepoints 22 | Data Stream API写的程序,可以从一个savepoint恢复执行。 Savepoints允许更新程序,并且让你的 Flink集群不丢失任何状态。 23 | 24 | Savepoints是手工触发的checkpoints, 它将程序的快照,写进state备份。他们依赖于规则的checkpointing机制。在程序运行的时候,会定时的,在worker节点产生快照和checkpoints。对于恢复,只需要上一个,已经完成的checkpoint。之前的checkpoints都可以安全的删除。 25 | 26 | Savepoints和这些定时的checkpoints很像,除了一点,Savepoints是用户触发的。Savepoints可以通过命令行创建,或者通过REST API取消一个job的时候。 27 | 28 | 参考: 29 | https://ci.apache.org/projects/flink/flink-docs-release-1.7/concepts/runtime.html 30 | 31 | 32 | -------------------------------------------------------------------------------- /Flink/Flink数据流编程模型_Window.md: -------------------------------------------------------------------------------- 1 | ## Windows 2 | 3 | streams上的聚合操作,(例如counts, sums等), 是被windows界定的, 例如 “count过去5分钟的数据”, “sum上100个元素”。 4 | Windows可以是time driven (例如: 每30秒) 或者 data driven (例如: 每100个元素)。一般会区分不同类型的window, 例如tumbling windows (没有重叠), sliding windows (有重叠), session windows (被不活跃的空隙所打断)。 5 | ## Time 6 | 当在streaming程序中,提到time,可能指的是不同的time: 7 | 8 | * Event Time,是event的创建时间。通常由events中的一个timestamp描述, 例如,由传感器附加的, 或者产生event的服务器。 9 | * Ingestion time,是在进行source操作时,event进入Flink数据流的时间。 10 | * Processing Time,是每个操作的本地时间。 11 | 12 | ## Stateful操作 13 | stateful操作的state(状态)维护在一个内置的key/value存储上。state被分区,并被分布式地和streams在一起。因此, 访问key/value state只能在keyed streams上,在keyBy()函数后面,并且,被限制在和当前event的key关联的values。对齐streams的keys和state,确保了所有的state更新都是本地操作,在没有额外开销的前提下,保证一致性。这种对齐,也使Flink redistribute state和透明地调整stream分区。 14 | ![](media/state_partitioning.svg) 15 | 16 | 17 | -------------------------------------------------------------------------------- /Flink/Flink数据流编程模型_抽象等级.md: -------------------------------------------------------------------------------- 1 | # 抽象等级 2 | Flink提供不同等级的抽象,开发流式、批处理程序。 3 | ![](media/levels_of_abstraction.svg) 4 | 5 | * 最低等级的抽象,提供有状态的数据流。它通过Process Function,嵌入在DataStream API中。 6 | 它允许用户自由的处理,从一个或多个流中过来的事件,并使用一致的容错机制。另外,用户可以注册事件时间和处理时间的回调,允许程序实现复杂的计算。 7 | 8 | * 在生产环境中,大多数程序不会用到上面的低阶抽象,而是会使用Core APIs,像DataStream API (有界/无界流) 、DataSet API (有界数据集)。这些APIs,为数据处理,提供通用的构建块的方法,例如,各种类型的transformations, joins, aggregations, windows, state等。这些APIs中的数据类型处理,表示为相应的编程语言中的类。 9 | * Table API是一个以表为中心的声明性DSL,可以动态的改变表(当处理流的时候)。Table API 遵循下面的关系模型: 表有一个附加的schema信息(就像关系型数据库中的表一样) 。API 提供可比较的操作,例如:select, project, join, group-by, aggregate等。Table API程序定义应该进行的操作,而不是操作的具体实现。Table API 可以被用户自定义的各种函数扩展,它的表现力不如 Core APIs, 但是用起来更简洁 (写更少的代码)。Table API程序,在执行前,同样也会根据优化器的规则进行优化。 10 | 11 | 用户可以无缝地在tables 和 DataStream/DataSet之间进行转换, 允许程序混合Table API、DataStream、DataSet APIs。 12 | 13 | * Flink提供的最高等级的抽象是SQL。在语义上和表现力上,这个抽象和Table API类似, 但是以SQL查询表达式代替程序。SQL抽象和Table API可以密切交互,SQL查询,可以运行在Table API定义的表上。 14 | 15 | 16 | -------------------------------------------------------------------------------- /Flink/Flink数据流编程模型_程序和数据流.md: -------------------------------------------------------------------------------- 1 | ## 程序和数据流 2 | Flink程序的基本构成块是streams(流)和transformations。(注意,Flink的DataSet API 使用的DataSets在内部也是streams) 在概念上,一个stream就是一个数据流, transformation是一个操作,该操作把一个或多个stream(流)作为输入,产生一个或多个stream(流)作为输出结果。 3 | 4 | 当执行的时候,Flink程序由streams和transformation操作组成。 5 | 每个数据流(dataflow)以一个或者多个sources作为开始,以一个或者多个sinks作为结束。数据流组成DAGs。 尽管通过迭代,允许特殊形式的循环,但为了简单起见,我们在大多数情况下对其进行掩饰。 6 | ![](media/program_dataflow.svg) 7 | ## 并行数据流 8 | Flink程序,在内部是分布式并行的。在执行时,一个stream有一个或者多个stream partitions。 9 | Stream的并行度,总是由生成它的操作符决定的。 10 | ![](media/parallel_dataflow.svg) 11 | 12 | Streams可以在两个操作符之间传输数据,传输数据可以,以one-to-one(或者forwarding) 方式, 或者redistributing方式: 13 | 14 | * One-to-one streams,像map操作 15 | * Redistributing streams,像keyBy操作,window操作,sink操作等。这些操作改变stream的partitions 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Flink/Flink是什么.md: -------------------------------------------------------------------------------- 1 | # Flink是什么 2 | Apache Flink 是一个框架,是一个分布式的处理引擎,它在无界和有界的数据流上,进行有状态的计算。Flink 被设计成,可以运行在所有的通用分布式环境中,Flink是内存式计算,拥有内存计算的快速特点。 3 | 4 | 下面,我们介绍下Flink架构的重要特征 5 | 6 | ## 处理无界和有界的数据 7 | 任何类型的数据,都是以一种事件流(event stream)的方式,产生的。 8 | 信用卡交易,传感器数据,机器的日志,或者用户在网站上、手机应用上的交互操作,所有这些数据都是以流(stream)的方式产生的。 9 | 10 | 数据可以被处理成无界和有界的流。 11 | 12 | ### 无界数据流 13 | 有一个明确的开始,但是没有明确的结束。 14 | 无界数据流,不会终止,只要有数据产生,就会一直提供。 15 | 无界数据流,必须被持续的处理,例如,事件(event)在被接收到后,必须被合理的处理。等待所有的输入数据到达,是不可能的,因为,输入数据是无界的,在任何时间点,输入都不会结束。 16 | 处理无界数据,经常需要,事件(event)以特定的顺序被接收,例如,以事件发生的顺序,这样能够推断结果的完整性。 17 | 18 | 19 | ### 有界数据流 20 | 有明确的开始和明确的结束。 21 | 有界数据流,可以先接收所有待处理的数据,然后一起再计算。 22 | 处理有界数据流的时候,并不需要顺序接收数据,因为,有界数据流,本身总是有序的。 23 | 处理有界数据流,也被称为批处理(batch processing) 24 | 25 | Apache Flink 擅长处理无界和有界数据。 26 | 对时间和状态(state)的精准控制,使Flink可以在无界数据流上,运行任何类型的程序。 27 | 有界数据流,被一些,专为固定数据集,设计的算法和数据结构处理,这样能有很好的性能。 28 | 29 | ### 部署应用程序 30 | Flink 是一个分布式的系统, 为了执行程序,需要计算资源。 31 | Flink集成了所有的,通用集群资源管理技术,例如Hadoop YARN,Apache Mesos,Kubernetes,同时,Flink也可以运行在stand-alone集群上。 32 | 33 | Flink,被设计成和上面列出的集群管理技术,无缝兼容。 34 | 这是通过,resource-manager-specific 部署模式实现的,这种部署模式,允许Flink以它惯用的方式和每个集群资源管理交互 35 | 36 | 当部署Flink程序的时候,Flink,根据程序配置的并行度(parallelism),自动识别出需要的资源,并向资源管理器(resource manager)申请资源。如果失败,Flink申请新的资源,替换掉失败的容器(container)。 37 | 所有的提交或者控制程序的通信,都是通过REST 调用。这简化了Flink在很多环境下的整合流程。 38 | 39 | ### 在任何数据规模下运行程序 40 | Flink被设计成,在任何数据规模下,运行有状态的流式(stateful)程序。 41 | 程序,并行化执行,可能有数千个任务(task)分布式的、并发的在集群上执行。因此,一个程序可以利用几乎所有的CPUs、内存、硬盘和网络IO。 42 | 并且,Flink很容易维护非常大的程序的状态。 43 | Flink的异步和增量checkpointing算法,在保证exactly-once 状态一致性的前提下,减小对处理延时的影响。 44 | 45 | 用户报告了,Flink运行在上产环境中,令人印象深刻的可扩展性。 46 | 例如: 47 | 48 | * 每天处理万亿级别事件的程序, 49 | * 维护太字节(兆兆字节)的状态的程序, 50 | * 运行在数千cores上的程序 51 | 52 | ### 充分利用内存性能 53 | 有状态的Flink程序,对于本地状态访问是可选的。 54 | 任务的状态(状态就是数据?),总是维护在内存中, 55 | 或者,当状态大小超过可用内存,维护在,访问高效的磁盘数据结构。 56 | 57 | 因此,任务(tasks)通过访问本地状态(本地,指的是task运行所在的节点),通常是内存状态,执行计算,具有非常低的处理延迟。 58 | 59 | 当任务失败的时候,Flink通过周期的、异步的checkpointing本地状态到持久化存储,保证exactly-once状态一致性。 60 | 61 | ![](media/Leverage_In-Memory_Performance.png) 62 | 63 | -------------------------------------------------------------------------------- /Flink/media/Leverage_In-Memory_Performance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaofateng/knowledge-without-end/79f8fa6ad3393cf8ece5ad5e1eac3d74344c2e22/Flink/media/Leverage_In-Memory_Performance.png -------------------------------------------------------------------------------- /Flink/media/levels_of_abstraction.svg: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 31 | 33 | 35 | 36 | 38 | image/svg+xml 39 | 41 | 42 | 43 | 44 | 45 | 48 | 51 | 55 | 59 | Stateful 65 | Stream Processing 71 | 75 | 79 | DataStream 85 | / 91 | DataSet 97 | API 103 | 107 | 111 | Table API 117 | 121 | 125 | SQL 131 | Core 137 | APIs 143 | Declarative DSL 149 | High 155 | - 161 | level Language 167 | Low 173 | - 179 | level building block 185 | (streams, state, [event] time) 191 | 192 | 193 | 194 | -------------------------------------------------------------------------------- /Flink/media/program_dataflow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 21 | 22 | 32 | 34 | 36 | 37 | 39 | image/svg+xml 40 | 42 | 43 | 44 | 45 | 46 | 49 | 52 | Source 58 | DataStream 64 | < 70 | String 76 | > 82 | lines = 88 | env. 94 | addSource 100 | ( 106 | new 112 | FlinkKafkaConsumer 118 | <>(…)); 124 | DataStream 130 | < 136 | Event 142 | > events = 148 | lines. 154 | map 160 | ((line) 166 | - 172 | > 178 | parse 184 | (line)); 190 | DataStream 196 | < 202 | Statistics 208 | > stats = events 214 | . 220 | keyBy 226 | ( 232 | "id" 238 | ) 244 | . 250 | timeWindow 256 | ( 262 | Time.seconds 268 | (10)) 274 | . 280 | apply 286 | ( 292 | new 298 | MyWindowAggregationFunction 304 | ()); 310 | stats. 316 | addSink 322 | ( 328 | new 334 | RollingSink 340 | (path)); 346 | 350 | Source 356 | 360 | map() 366 | 370 | 374 | 378 | 382 | 386 | Transformation 392 | Transformation 398 | Source 404 | Operator 410 | 414 | 418 | keyBy 424 | ()/ 430 | window()/ 436 | apply() 442 | 446 | 450 | Sink 456 | 460 | Transformation 466 | Operators 472 | Sink 478 | Operator 484 | 488 | 492 | 496 | 500 | 504 | Stream 510 | 514 | 518 | Sink 524 | 528 | Streaming Dataflow 534 | 544 | 545 | 546 | 547 | -------------------------------------------------------------------------------- /Flink/media/state_partitioning.svg: -------------------------------------------------------------------------------- 1 | 2 | 20 | 21 | 31 | 33 | 35 | 36 | 38 | image/svg+xml 39 | 41 | 42 | 43 | 44 | 45 | 48 | 51 | key/value 57 | state 63 | 67 | Source 73 | [1] 79 | 83 | 87 | S 93 | tateful 99 | [1] 105 | 109 | Source 115 | [2] 121 | 125 | 129 | Stateful 135 | [2] 141 | 145 | 149 | 153 | D … 159 | E 165 | 171 | Z 177 | 183 | 187 | 191 | 195 | A … 201 | B 207 | 213 | Y 219 | 225 | 229 | 233 | 237 | 241 | Keys(A,B,Y) 247 | 251 | 255 | Keys(D,E,Z) 261 | 265 | Redistribution 271 | [ 277 | keyBy 283 | ()] 289 | 290 | 291 | 292 | -------------------------------------------------------------------------------- /Hyperledger/Channel 配置 (configtx) .md: -------------------------------------------------------------------------------- 1 | # Channel 配置(configtx) 2 | 3 | **Hyperledger Fabric区块链网络的共享配置,存储在collection configuration transactions中,每个Channel一个。** 每个configuration transaction 通常使用一个较短的名称`configtx`。 4 | 5 | Channel配置具有以下重要属性: 6 | 7 | * Versioned(版本化):配置文件中的所有元素,都有一个关联的版本,每次修改都会更新版本号。 此外,每个提交的配置都会收到一个序列号。 8 | 9 | * Permissioned(权限):配置文件中的每个元素,都有一个关联的策略,用于控制是否允许对该元素进行修改。 任何拥有之前configtx副本(并且没有其他额外的信息)的人,都可以根据这些策略验证新配置的有效性。 10 | 11 | * Hierarchical(分层):根配置组包含子组,并且每个层次组都有关联的值和策略。 这些政策可以利用层次结构,从较低层次的政策中生成较高层次的政策(These policies can take advantage of the hierarchy to derive policies at one level from policies of lower levels)。 12 | 13 | 14 | ## 详解一个配置 15 | 16 | 配置作为`HeaderType_CONFIG`类型的transaction,存储在没有其它transaction的block(块)中。 这些块被称为Configuration Blocks(配置块),其中第一块被称为Genesis Block(创世区块)。 17 | 18 | 配置的原型结构(proto structures)存储在`fabric/protos/common /configtx.proto`中。` HeaderType_CONFIG`类型的Envelope(信封、包、封装)将`ConfigEnvelope`消息编码为`Payload`数据字段。 `ConfigEnvelope`的原型定义如下: 19 | 20 | ```c 21 | message ConfigEnvelope { 22 | Config config = 1; 23 | Envelope last_update = 2; 24 | } 25 | ``` 26 | 27 | `last_update`字段在下面的 “Updates to configuration(更新到配置)” 部分中定义,它仅在验证配置时需要。 相反,当前提交的配置,存储在`config`字段中,其中包含`Config`消息。 28 | 29 | ```c 30 | message Config { 31 | uint64 sequence = 1; 32 | ConfigGroup channel_group = 2; 33 | } 34 | ``` 35 | 36 | 对于每个已提交的配置,序号(sequence numbe)都加1。 `channel_group`字段是包含配置的根group。 `ConfigGroup`结构是递归定义的,并构建一个group的树,其中每个group都包含值和策略。 它被定义如下: 37 | 38 | ```c 39 | message ConfigGroup { 40 | uint64 version = 1; 41 | map groups = 2; 42 | map values = 3; 43 | map policies = 4; 44 | string mod_policy = 5; 45 | } 46 | ``` 47 | 48 | 由于`ConfigGroup`是一个递归结构,它具有分层结构。 下面的例子是为了使golang符号更清晰。 49 | 50 | ```c 51 | // 假设下面的 groups 如下定义 52 | //var root, child1, child2, grandChild1, grandChild2, grandChild3 *ConfigGroup 53 | 54 | // 设置下面的 values 55 | root.Groups["child1"] = child1 56 | root.Groups["child2"] = child2 57 | child1.Groups["grandChild1"] = grandChild1 58 | child2.Groups["grandChild2"] = grandChild2 59 | child2.Groups["grandChild3"] = grandChild3 60 | 61 | // groups的config structure 结果像下面: 62 | // root: 63 | // child1: 64 | // grandChild1 65 | // child2: 66 | // grandChild2 67 | // grandChild3 68 | ``` 69 | 70 | 每个group都在配置层次结构中定义一个级别,每个group都有一组关联的value(由字符串key索引)和Policies(策略,也由字符串key索引)。 71 | 72 | values由以下定义: 73 | 74 | ```c 75 | message ConfigValue { 76 | uint64 version = 1; 77 | bytes value = 2; 78 | string mod_policy = 3; 79 | } 80 | ``` 81 | Policies 由以下定义: 82 | 83 | ```c 84 | message ConfigPolicy { 85 | uint64 version = 1; 86 | Policy policy = 2; 87 | string mod_policy = 3; 88 | } 89 | ``` 90 | 91 | 请注意,Values, Policies, 和 Groups都有一个版本和一个`mod_policy`。 每次元素被修改时,元素的版本都会增加。 `mod_policy`用于管理修改该元素所需的签名(signatures)。 92 | 93 | 对于Group,修改是向Values, Policies, or Groups maps添加或删除元素(或更改`mod_policy`)。 94 | 95 | 对于Values and Policies,修改是分别更改`Value` 和 `Policy`字段(或更改`mod_policy`)。 每个元素的`mod_policy`都是在当前级别config的上下文中进行评估的。 96 | 97 | 考虑在`Channel.Groups [“Application”]`处定义的以下示例mod policies(mod策略)(这里,我们使用golang map参考语法,因此`Channel.Groups [“Application”].Policies [“policy1”]`引用基本`Channel` group的`Application ` group的`Policies` 的 `policy1` policy。) 98 | 99 | * `policy1` maps to `Channel.Groups["Application"].Policies["policy1"]` 100 | * `Org1/policy2` maps to `Channel.Groups["Application"].Groups["Org1"].Policies["policy2"]` 101 | * `/Channel/policy3` maps to `Channel.Policies["policy3"]` 102 | 103 | 注意,如果 mod_policy 引用一个不存在的 policy , 条目不会被修改。 104 | 105 | ## Configuration 更新 106 | 107 | Configuration更新,作为`HeaderType_CONFIG_UPDATE`类型的`Envelope`(信封)消息被提交。 transaction的`Payload`数据是一个编组的`ConfigUpdateEnvelope`。 `ConfigUpdateEnvelope`定义如下: 108 | 109 | ```c 110 | message ConfigUpdateEnvelope { 111 | bytes config_update = 1; 112 | repeated ConfigSignature signatures = 2; 113 | } 114 | ``` 115 | 116 | `signatures`字段,包含授权配置更新的一组signatures(签名)。 其消息定义是: 117 | 118 | ```c 119 | message ConfigSignature { 120 | bytes signature_header = 1; 121 | bytes signature = 2; 122 | } 123 | ``` 124 | 125 | `signature_header`与标准transactions定义的一样,但signature位于`signature_header`字节和`ConfigUpdateEnvelope`消息的`config_update`字节的连接之上。 126 | 127 | `ConfigUpdateEnvelope` `config_update`字节是封装的(marshaled)`ConfigUpdate`消息,其定义如下: 128 | 129 | ```c 130 | message ConfigUpdate { 131 | string channel_id = 1; 132 | ConfigGroup read_set = 2; 133 | ConfigGroup write_set = 3; 134 | } 135 | ``` 136 | 137 | channel_id是,更新需要绑定的Channel ID。 138 | 139 | * `read_set`指定现有配置的一个子集,指定了,只有版本字段,且没有其它字段必须填充的稀疏部分。`read_set`中不应该设置特定的`ConfigValue`值或`ConfigPolicy`策略字段。 140 | `ConfigGroup`可能会填充其映射字段的子集,以便引用配置树中更深的元素。 例如,要将`Application` group 包含在`read_set`中,它的父级(`Channel` group)也必须包含在`read_set`中,但`Channel` group不需要填充所有keys,例如`Orderer` group key, 或任何`values` 或 `policies `keys。 141 | 142 | * `write_set`指定修改的配置片段。 由于配置的层次性,对层次结构中深层元素的写入操作,也必须在其`write_set`中包含更高级别的元素。 但是,对于`read_set`中同样版本中指定的`write_set`中的任何元素,该元素应该被指定为稀疏,就像在`read_set`中一样。例如,给定配置: 143 | 144 | ```c 145 | Channel: (version 0) 146 | Orderer (version 0) 147 | Appplication (version 3) 148 | Org1 (version 2) 149 | ``` 150 | 151 | 提交一个修改 `Org1` 的configuration update, `read_set` 是: 152 | 153 | ```c 154 | Channel: (version 0) 155 | Application: (version 3) 156 | ``` 157 | `write_set`是: 158 | 159 | ```c 160 | Channel: (version 0) 161 | Application: (version 3) 162 | Org1 (version 3) 163 | ``` 164 | 165 | 当收到`CONFIG_UPDATE`时,orderer通过执行以下操作来计算生成的`CONFIG`: 166 | 167 | 1. 验证`channel_id`和`read_set`。 `read_set`中的所有元素都必须存在于给定的版本中。 168 | 2. 通过收集`write_set`中所有不在read_set中显示为相同版本的元素来计算`update set`。 169 | 3. 验证`update set`中的每个元素,通过加1更新元素的版本号。 170 | 4. 验证附加到`ConfigUpdateEnvelope`的`signature set`,满足`update set`中每个元素的`mod_policy`。 171 | 5. 通过将`update set`应用于当前配置,来计算新的完整配置版本。 172 | 6. 将新配置写入一个`ConfigEnvelope`,其中包括`CONFIG_UPDATE`作为`last_update`字段,以及配置字段中编码的新配置,以及递增的序列值。 173 | 7. 将新的`ConfigEnvelope`写入`CONFIG`类型的`Envelope`中,并最终将其作为单个事务写入新configuration block中。 174 | 175 | 当peer(或任何其它`Deliver`的receiver)接收到此configuration block时,它应该通过,将`last_update`消息应用于当前配置,并验证`orderer-computed` `config`字段包含正确的新配置,来验证配置是否有效。 176 | 177 | 178 | ## Permitted configuration groups and values(允许的配置组和值) 179 | 180 | 任何有效的配置都是以下配置的一个子集。 这里我们使用记号`peer.`来定义一个`ConfigValue`,其值域是在`fabric/protos/peer/configuration.proto`中定义的名为的封装的proto消息。 181 | 182 | `common.` `msp.`和`orderer.`与上面的类似,但是它们的消息在 183 | `fabric/protos / common/configuration.proto`, 184 | `fabric/protos/msp/ mspconfig.proto`, 185 | `fabric/protos/orderer/ configuration.proto`中相应定义。 186 | 187 | 请注意,关键字`{{org_name}}`和`{{consortium_name}}`表示任意名称,并表示可能以不同名称重复的元素。 188 | 189 | ```c 190 | &ConfigGroup{ 191 | Groups: map { 192 | "Application":&ConfigGroup{ 193 | Groups:map { 194 | {{org_name}}:&ConfigGroup{ 195 | Values:map{ 196 | "MSP":msp.MSPConfig, 197 | "AnchorPeers":peer.AnchorPeers, 198 | }, 199 | }, 200 | }, 201 | }, 202 | "Orderer":&ConfigGroup{ 203 | Groups:map { 204 | {{org_name}}:&ConfigGroup{ 205 | Values:map{ 206 | "MSP":msp.MSPConfig, 207 | }, 208 | }, 209 | }, 210 | 211 | Values:map { 212 | "ConsensusType":orderer.ConsensusType, 213 | "BatchSize":orderer.BatchSize, 214 | "BatchTimeout":orderer.BatchTimeout, 215 | "KafkaBrokers":orderer.KafkaBrokers, 216 | }, 217 | }, 218 | "Consortiums":&ConfigGroup{ 219 | Groups:map { 220 | {{consortium_name}}:&ConfigGroup{ 221 | Groups:map { 222 | {{org_name}}:&ConfigGroup{ 223 | Values:map{ 224 | "MSP":msp.MSPConfig, 225 | }, 226 | }, 227 | }, 228 | Values:map { 229 | "ChannelCreationPolicy":common.Policy, 230 | } 231 | }, 232 | }, 233 | }, 234 | }, 235 | 236 | Values: map { 237 | "HashingAlgorithm":common.HashingAlgorithm, 238 | "BlockHashingDataStructure":common.BlockDataHashingStructure, 239 | "Consortium":common.Consortium, 240 | "OrdererAddresses":common.OrdererAddresses, 241 | }, 242 | } 243 | ``` 244 | 245 | ## Orderer system channel configuration 246 | 247 | ordering system channel需要定义ordering参数, 和用于创建channel的联盟。 一个 ordering service必须有一个ordering system channel,并且它是第一个要创建的Channel(或更精确地说是bootstrapped)。 建议不要在ordering system channel生成配置中定义Application部分,但在测试时候,可以添加。 请注意,任何对ordering system channel具有读权限的成员,都可以看到所有Channel创建,因此应该限制Channel的访问权限。 248 | 249 | ordering 参数被定义为config的以下子集: 250 | 251 | ```c 252 | &ConfigGroup{ 253 | Groups: map { 254 | "Orderer":&ConfigGroup{ 255 | Groups:map { 256 | {{org_name}}:&ConfigGroup{ 257 | Values:map{ 258 | "MSP":msp.MSPConfig, 259 | }, 260 | }, 261 | }, 262 | 263 | Values:map { 264 | "ConsensusType":orderer.ConsensusType, 265 | "BatchSize":orderer.BatchSize, 266 | "BatchTimeout":orderer.BatchTimeout, 267 | "KafkaBrokers":orderer.KafkaBrokers, 268 | }, 269 | }, 270 | }, 271 | ``` 272 | 273 | 参与ordering的每个organization都在Orderer组下有一个group元素。 该group定义了单个参数MSP,其中包含该organization的加密身份信息。 Orderer group的值决定了ordering节点的功能。 它们存在于每个channel中,因此orderer.BatchTimeout可以在不同的Channel上有不同的值。 274 | 275 | 在启动时,orderer面对的是包含许多Channel信息的文件系统。 orderer通过识别定义consortiums group的Channel,来识别system channel。 consortiums group具有以下结构。 276 | 277 | ```c 278 | &ConfigGroup{ 279 | Groups: map { 280 | "Consortiums":&ConfigGroup{ 281 | Groups:map { 282 | {{consortium_name}}:&ConfigGroup{ 283 | Groups:map { 284 | {{org_name}}:&ConfigGroup{ 285 | Values:map{ 286 | "MSP":msp.MSPConfig, 287 | }, 288 | }, 289 | }, 290 | Values:map { 291 | "ChannelCreationPolicy":common.Policy, 292 | } 293 | }, 294 | }, 295 | }, 296 | }, 297 | }, 298 | ``` 299 | 300 | 请注意,每个consortium(联盟)都会定义一组members(成员),就像ordering orgs的organizational members一样。 每个联盟还定义一个`ChannelCreationPolicy`,这是一个应用于授权Channel创建请求的策略。 通常,此值将设置为`ImplicitMetaPolicy`,要求Channel的新成员签名,以授权Channel的创建。 有关Channel创建的更多细节将在本文后面介绍。 301 | 302 | ## Application channel configuration 303 | Application配置,适用于为Application类型的transactions设计的Channel。它被定义如下: 304 | 305 | ```c 306 | &ConfigGroup{ 307 | Groups: map { 308 | "Application":&ConfigGroup{ 309 | Groups:map { 310 | {{org_name}}:&ConfigGroup{ 311 | Values:map{ 312 | "MSP":msp.MSPConfig, 313 | "AnchorPeers":peer.AnchorPeers, 314 | }, 315 | }, 316 | }, 317 | }, 318 | }, 319 | } 320 | ``` 321 | 322 | 就像Orderer部分一样,每个organization都被编码为一个group。 然而,不是仅对MSP身份信息进行编码,而是每个org还编码一列 `AnchorPeers`。 该列表允许不同org的peer彼此联系,以进行peer gossip networking。 323 | 324 | application channel对orderer orgs和consensus options的副本,进行编码以允许确定性地更新这些参数,因此包含了与orderer system channel configuration相同的Orderer部分。 但从应用角度来看,这可能在很大程度上被忽略。 325 | 326 | 327 | ## Channel creation 328 | 329 | 当orderer收到一个不存在的Channel的CONFIG_UPDATE时,orderer就认为这是channel creation请求并执行以下操作。 330 | 331 | 1. orderer识别要执行channel creation请求的`Consortium`。它通过查看上级group 的Consortium value来完成。 332 | 2. orderer验证包含在`Application` group 中的organizations,是相应consortium中包含的organizations的子集,并且`ApplicationGroup`被设置为版本1。 333 | 3. orderer验证,如果consortium拥有members,新Channel也有application members(创建没有member的consortium和Channel 仅对测试有用)。 334 | 4. orderer通过ordering system channel中获取Orderer group,创建一个模板configuration。使用新指定的members,创建`Application` group。为`ChannelCreationPolicy`指定`mod_policy`,如consortium config中指定的那样。请注意,policy是在新配置的上下文中进行评估的,因此需要所有members的策略,需要来自所有新Channel成员的签名,而不是所有consortium成员的签名。 335 | 5. orderer然后将`CONFIG_UPDATE`作为此模板配置的更新应用。由于`CONFIG_UPDATE`对Application group(其版本为1)进行了修改,配置代码根据`ChannelCreationPolicy`验证这些更新。如果Channel创建包含任何其它修改,例如对单个org的anchor peers,则将调用该元素的相应mod policy。 336 | 6. 具有新Channel配置的新`CONFIG` transaction 将被打包并发送,以便在ordering system channel上进行ordering。ordering后,channel就创建了。 337 | 338 | 339 | 欢迎加入区块链技术交流QQ群 694125199 340 | 341 | 更多区块链知识: 342 | https://github.com/xiaofateng/knowledge-without-end/tree/master/区块链/Hyperledger 343 | 344 | 本文参考 345 | http://hyperledger-fabric.readthedocs.io/en/latest/configtx.html 346 | 347 | 348 | -------------------------------------------------------------------------------- /Hyperledger/ConfigEnvelope.md: -------------------------------------------------------------------------------- 1 | # ConfigEnvelope 2 | 3 | ConfigEnvelope被设计为,不依赖之前的configuration transactions的,包含一个chain的_all_配置。 4 | 5 | 它用以下方案生成: 6 | 1. 检索现有配置 7 | 2. note要修改的配置属性(ConfigValue,ConfigPolicy,ConfigGroup) 8 | 3. 向ConfigUpdate.read_set(稀疏)添加任何中间ConfigGroups 9 | 4. 向ConfigUpdate.read_set(稀疏)添加其它所需的依赖项 10 | 5. 修改配置属性,将每个版本递增1,并将它们设置在ConfigUpdate.write_set中。注意:任何没有修改但指定的元素,应该已经在read_set中,因此可以指定为稀疏 11 | 6. 创建ConfigUpdate消息,并将其编组到ConfigUpdateEnvelope.update。更新和编码所需的签名 12 | *每个签名都是ConfigSignature类型 13 | *ConfigSignature签名位于signature_header和ConfigUpdate字节(它包含一个ChainHeader)的连接之上, 14 | 9. 提交新的Config,以在由提交者签名的Envelope中进行排序 15 | * Envelope Payload将数据,设置为编组的ConfigEnvelope 16 | * Envelope Payload有一个类型为Header.Type.CONFIG_UPDATE的header 17 | 18 | configuration manager(配置管理器)将验证: 19 | 1. read_set中的所有项都存在于读取版本中 20 | 2. write_set中与read_set不同版本的所有项,或者不read_set中的项,已根据其mod_policy进行了适当的签名 21 | 3.新的configuration满足ConfigSchema 22 | 23 | 24 | -------------------------------------------------------------------------------- /Hyperledger/Hyperledger Composer介绍.md: -------------------------------------------------------------------------------- 1 | # Hyperledger Composer 2 | 是一系列的,用于构建区块链商业网络的协同工具。它帮助商业主更简单、更快的构建,帮助开发者创建智能合约和区块链应用来解决商业问题。它用JavaScript,和更现代化的工具,包括node.js、npm、CLI等。Composer提供的是一个商业抽象,具体可以参考汽车商业的例子。 3 | 4 | ## 通过使用Hyperledger Composer,一个商人可以和开发者合作, 5 | 1. 定义在区块链使用场景中的资产 6 | 2. 定义商业规则,来决定什么样的交易是可以的 7 | 3. 定义参与者、身份识别和访问权限的控制,存在哪些角色,哪些角色可以执行哪种类型的交易 8 | 9 | ## 开发者做如下的事情: 10 | 1. 做出可重用的、商业网络中的核心组件—资产、参与者、交易逻辑和商业网络的访问控制。然后,这个商业网络可以在多个公司之间进行分享。 11 | 2. 生成JavaScript和REST APIs,以便和程序、遗留系统交互。创建概要程序,并在商业网络上运行分析。 12 | 3. 开始开发和测试,然后把这个商业网络部署到线上的Hyperledger Fabric或者其它的区块链网络实例上。 13 | 14 | ## 采用Hyperledger Composer的区块链客户端,有如下的优点: 15 | 1. 更快的创建区块链程序 16 | 2. 经过良好的测试、高效的设计,减少风险 17 | 3. 高度的抽象,更容易迭代,可扩展性高 18 | Hyperledger Composer包括下面几个主要组件: 19 | 1. 商业网络压缩包:抓取商业网络中的核心数据,包括商业模型、交易逻辑、访问控制。商业网络压缩包把这些元素打包起来,并把它们部署运行。 20 | 2. Composer场地:这个基于网络的工具,允许开发者学习Hyperledger Composer、为商业网络的建模,测试网络并部署到线上环境。Composer场地提供了一个商业网络样例的库,可以据此创建自己的商业网络。 21 | 3. REST API支持和整合的能力:一个商业网络的LoopBack连接已经被开发好了,它把一个运行的商业网络,作为REST API的方式暴露出来。这样商业网络可以和客户端程序交互,也可以很容易的和非区块链程序整合。 22 | 23 | -------------------------------------------------------------------------------- /Hyperledger/Hyperledger Fabric 1.0架构介绍.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 简介 4 | 本文主要介绍了Fabric1.0中的重大变化和架构。 5 | Fabric1.0版本中,把节点分为peers节点(维护state、ledger)和orderers节点(负责对ledger中的transactions达成共识)。在Fabric0.6和之前的版本中,没有这一概念。 6 | 7 | 介绍了Endorsing peers,它作为一类特殊的peers,负责同时执行chaincode和endorsing transactions。 8 | # 新架构的优点 9 | 新架构有下面的优点: 10 | * Chaincode信任的灵活性。 11 | 12 | 该架构将Chaincode的信任假设和ordering的信任假设分离开。 13 | 也就是说,ordering service可以被一组节点提供,并且容忍其中的一些节点fail或者出现异常;同时对于每一个Chaincode,endorsers都可以是不同的。 14 | 15 | 联系实际的使用场景,多个组织加入了同一个区块链网络中,并且各自提供了自己机器作为网络中的各种节点,这些节点可以配置成peers节点,也可以配置为orderers节点,是分散的。 16 | 17 | 如果网络中的所有peers和为orderers节点都在同一个组织中,如果这个组织的集群出现故障,那么会影响整个区块链网络,或者这个组织作弊,那么其它节点很难发现。 18 | 19 | * 可扩展性 20 | 21 | **当不同的Chaincodes指定了不关联的endorsers,可以在endorsers间对Chaincode进行分区,并允许Chaincode的并行执行。** 22 | 同时,Chaincode的执行可能消耗很大,把它从ordering service这个关键路径中移除是明智的。 23 | 24 | * 保密性 25 | 26 | 该体系结构有助于部署,对于其交易的内容和状态更新具有保密要求的Chaincode。 27 | 28 | * 共识机制模块化 29 | 30 | 该架构师模块化的,允许可插拔的共识机制实现(例如ordering service) 31 | 32 | 下面要介绍的内容,一部分是1.0版本中的,一部分是1.0版本以后的。 33 | 34 | # 目录 35 | Part I: Hyperledger Fabric v1架构相关的概念 36 | 37 | * 系统架构 38 | 39 | * Transaction endorsement的基本工作流 40 | 41 | * Endorsement policies 42 | 43 | Part II: 1.0版本后续架构的概念 44 | 45 | * Ledger checkpointing (pruning) 46 | 47 | ## 1.系统架构 48 | 49 | Blockchain运行的程序叫Chaincode。Chaincode是核心元素,因为transactions是对Chaincode上的操作的调用。 50 | Transations 必须被“endorsed”,只有endorsed的transactions(被认可的交易)才能被提交。 51 | 会存在一个或多个特殊的Chaincode,来管理函数和参数,统称为system Chaincode。 52 | 53 | ## 1.1. Transactions 54 | Transactions 有以下两种类型: 55 | 56 | 部署 transactions:创建新的chaincode,并把程序作为参数。 当一个部署 transaction 执行成功后, chaincode就被安装在了blockchain上。 57 | 58 | 调用 transactions:在上一步部署好的Chaincode上执行操作。Chaincode执行特定的函数,这个函数可能调用对相应状态的修改,并返回结果。 59 | 60 | 后面会介绍到,部署transactions是调用transactions的特殊情况,创建新Chaincode的部署transactions,对应于System Chaincode上的调用transaction。 61 | 62 | 注:本文没有介绍: 63 | a) query (read-only) transactions 的优化 (包含在1.0版本中)。 64 | b) 支持cross-chaincode的transactions (1.0后续版本中的特性。 65 | 66 | ## 1.2. Blockchain 数据结构 67 | ### 1.2.1. State(状态) 68 | blockchain的状态是版本化的 key/value store (KVS), keys 是名字,values是任意的 blobs,版本号标识这条记录的版本。 这些数据项,由Chaincode通过put和get KVS-操作来管理. state是持久化存储的,对state的更新是被log记录的。 69 | 更具体点,state的模型是一个mapping K -> (V X N), : 70 | 71 | * K 是一组keys 72 | * V 是一组values 73 | * N is 一组无限的,有序的版本号. Injective函数 `next: N -> N` 根据N 返回下一个版本号。 74 | `V` 和 `N` 都包含一个特殊的元素 `\bot`, 这在N是最低元素的情况下 75 | . 初始情况下,所有的 keys 都映射为 `(\bot,\bot)`。对于` s(k)=(v,ver) ` 我们使用`s(k).value`表示 `v`, 使用` s(k).version`表示 `ver`。 76 | 77 | KVS 操作具体如下: 78 | 79 | * `put(k,v)`, 把 blockchain 的状态` s` 变成` s'`,像下面这样 `s'(k)=(v,next(s(k).version))` 80 | * `get(k)` 返回 `s(k)` 81 | 82 | State 是被peers管理的(state就是存储在peer上的), 而不是被orderers 和 clients。 83 | 84 | **State partitioning** 85 | KVS 中的Keys,可以从它们的名字,识别出属于属于哪一个特定的chaincode, 只有特定Chaincode的transaction,才能修改属于它的keys。 原则上, 任何 chaincode 都能够读取属于其它 chaincodes的keys。cross-chaincode的transactions, 即修改属于其它chaincodes的state是v1后续版本的功能。 86 | 87 | ### 1.2.2 Ledger 88 | Ledger 提供所有成功的state改变,和不成功的尝试改变的历史。 89 | 90 | **Ledger 被ordering service (see Sec 1.3.3) 构建成一个完全有序的,(有效或无效)transaction 块组成的hash chain。** Hashchain 强加了ledger中blocks的完全有序性。每个block 又包含一个完全有序的transactions的数组, 这又整体上强加了所有transaction的完全有序性。 91 | 92 | **Ledger存储在所有的peers上, 也可以选择存储在几个orderers上**。 在orderer的上下文中,我们将OrdererLedger统称为Ledger, 在 peer 的上下文中,我们将PeerLedger统称为Ledger。 PeerLedger 与 the OrdererLedger 不同,peers 本地维护着一个 bitmask 来区分有效transactions和无效transaction。(XX章节会详细介绍). 93 | 94 | Peers 可能会修剪(去掉?) PeerLedger( XX章节描述了这点,它是v1后续版本的特性). Orderers维护OrdererLedger,可以增加整个系统的容错性和可用性,并且可以随时决定修剪它,只要维护ordering service的属性即可。 95 | 96 | Ledger允许重做所有transactions的历史记录,并且重建state。 97 | 98 | ### 1.3. Nodes(节点) 99 | Nodes是blockchain的通信实体。一个"node" 仅仅是一个逻辑上的功能,多个不同类型的node可以运行在同一个物理server上。重要的是node如何分组在“信任域(trust domains)”中并与控制它们的逻辑实体相关联。 100 | 101 | 有三种类型的nodes: 102 | 103 | * Client or submitting-client: 104 | 105 | 提交真正的transaction-invocation 到 endorsers, **广播 transaction-proposals 到 ordering service**。 106 | 107 | * Peer: 108 | 109 | 提交transactions ,维护state 和 ledger的拷贝(Ledger在每个peer上都有,且是同步的,某一个peer上的ledger,可能是从leader peer上同步过来的)。 除此之外,peer还有特殊的 endorser 角色。 110 | 111 | * Ordering-service-node or orderer: 112 | 113 | 运行实现了分发保证的通信服务,例如atomic 或者 total order 的广播. 114 | #### 1.3.1. Client(客户端) 115 | 客户端为了和blockchain通信,必须和peer连接。 The client may connect to any peer of its choice. 客户端创建,并从而invoke(调用) transactions. 116 | 117 | **第二章节会详细介绍, 客户端和 peers、 ordering service都会通信。** 118 | 119 | #### 1.3.2. Peer 120 | 一个 peer 从ordering service那里,以block的形式,接收有序的state更新。并且维护state和ledger。 121 | 122 | Peers 可以同时担当一个特殊的角色endorser。endorsing peer 的特殊功能是针对特定的chaincode,包含在一个transaction被提交前,对它背书、认可。 **每个chaincode都可以指定endorsement policy(背书策略)** (背书策略是引用一组endorsing peers的)。 123 | 124 | 策略定义了一个有效transaction endorsement(一般是一组endorsers的签名)的充分必要条件,二三章节会详细介绍这点。 **在install Chaincode的部署transactions的特殊情况下,(部署)背书策略被指定为System Chaincode的认可策略。** 125 | 126 | #### 1.3.3. Ordering service nodes (Orderers) 127 | Orderers 组成了 ordering service, 它提供可靠的通信网络。 Ordering service 可以用不同的方式实现: 中心化的服务(主要用户开发和测试) 、分布式的方式(用来应对不同的网络和节点的容错性)。 128 | 129 | **Ordering service为clients 和 peers 提供一个共享的通信 channel, 提供一个包含transaction的消息的广播服务。** Clients 连接到 channel,并且可以在该Channel上广播消息,然后将消息传递给所有peers。 Channel 支持所有消息的原子分发(有序分发)。 换而言之, channel 对所有连接到它的peers,发送相同的、商业逻辑上有序的消息。 **这种原子性的通信保证,也叫做全序广播, 原子广播, 或者分布式系统下的共识机制(我们通常都叫做共识机制) 130 | 。** 131 | 通信的消息,就是会包含在blockchain state中的候选transaction。 132 | 133 | **Partitioning (ordering service channels)** 134 | Ordering service 可以支持多个channels ,就像发布-订阅消息系统一样。 Clients 可以连接到一个指定的channel,然后发送消息和获取到达的消息。 Channels可以被看做 partitions - 连接到一个Channel的Client不知道其他Channel的存在,但client可能连接到多个Channel。 135 | 136 | 尽管Fabric v1中的ordering service 的实现支持多个channels, 为了简化介绍,本文后面的内容,我们假设 ordering service 由一个单独的channel/topic组成。 137 | 138 | **Ordering service API** 139 | Peers通过ordering service提供的接口,连接到ordering service提供的Channel。Ordering service API 由两个基本操作组成(更通用的异步事件): 140 | 141 | * `broadcast(blob)`: client 调用它来广播一条任意的消息块,在Channel上传播。 142 | * `deliver(seqno, prevhash, blob)`: Ordering service 在peer上调用这个接口,使用指定的非负整数序列(seqno)和最近交付的的blob(可以理解为操作transaction)的hash(prevhash)来发送消息blob。换而言之, 它是ordering service的输出event。deliver() 有时在pub-sub 系统中也叫做 notify() ,或者在 BFT 系统中叫做commit() 。 143 | 144 | 145 | **通常情况下,为了效率的提升, ordering service 会对 blobs 分组(分批次),然后在一个单独的deliver事件中输出blocks,而不是输出每个独立的transactions(也就是blobs)。**在这种情况下,ordering service必须在每个block中,强加和传达一个确定的blobs的顺序。每个block中的blobs的个数可以被ordering service 动态指定。 146 | 147 | 在下文中,为了便于表达,我们定义订ordering service的属性(本小节的其余部分),并解释transaction endorsement的工作流程(第2节)(假设每个deliver事件有一个blob的情况下,解释的工作流)。 根据上面提到的一个block的blob的确定性排序,工作流的解释很容易扩展到Block。 148 | 149 | **Ordering service 属性** 150 | 151 | Ordering service 有如下的保证: 152 | 153 | * 安全性(一致性保证)。尽管peers会断开连接或者宕机,但是它们会重新连接和启动。除非某些客client(或者peer)实际调用广播(blob),否则不发送 blob,并且最好每个Channel的blob只发送一次。此外, `deliver()` 事件包含上一次`deliver()` 事件(`prevhash`)数据的加密hash。`prevhash`是 序列号为`seqno-1`的`deliver()` 事件参数的加密hash。 这将在`deliver(`)事件之间建立hash chain,用于帮助验证ordering service输出的完整性,后面的第4节和第5节中会介绍。第一个`deliver()` 事件的特殊情况下, `prevhash` 有一个默认值。 154 | 155 | * Liveness (delivery 保证)。 Liveness保证是由具体的ordering service实现达到的。准确的保证需要依赖网络和节点的容错性。 156 | ## 2. Transaction endorsement的基本工作流 157 | 下面我们概要介绍一下transaction的high-level请求。 158 | 159 | 注意:下面的协议没有假设所有的transactions都是确定的, 也就是说,它允许不确定的transactions。 160 | 161 | ### 2.1. client 创建一个 transaction 并且发送到它选择的endorsing peers 162 | 为了调用transaction, client 发送一个 PROPOSE 消息到它选择的一组endorsing peers(可能并不是同时 - 见 2.1.2. 和 2.3.节)。**对于指定chaincodeID的endorsing peers组,client通过peer得到,对方又通过endorsement policy发现endorsing peers(详见3章节)。** 例如, transaction 可以发送给一个指定chaincodeID的所有的endorsers。 这就是说, 一些endorsers 可以是offline的, 其它的可能反对并选择不支持transaction 。 发出Submitting 的client 会去满足可用的endorsers的政策表达 。 163 | 164 | 下面, 我们首先详细介绍PROPOSE消息格式,然后讨论submitting client和endorsers之间可能的交互模式。 165 | #### 2.1.1. PROPOSE 消息格式 166 | 一个PROPOSE的消息格式是:``, `tx` 是必须的 `anchor` 是可选的参数。下面详细介绍: 167 | 168 | * `tx=` 169 | 170 | `clientID`是submitting client的`ID`, 171 | `chaincodeID` 指的是transaction所属的 chaincode 172 | `txPayload` 是包含 submitted transaction本身的有效负载。 173 | `timestamp` 是由客户维护的单调递增(对于每个新transaction)整数。 174 | `clientSig` 是client 端的签名。 175 | 176 | txPayload的细节,在调用transaction和部署transaction之间有所不同。 177 | 178 | 对于调用transaction,txPayload将由2个字段组成: 179 | `txPayload = `, 180 | `operation` 是指 chaincode 操作(函数) 和参数。 181 | `metadata` 是指和调用相关的属性。 182 | 183 | 对于部署 transaction, txPayload 由3个字段组成: 184 | `txPayload = `, 185 | `source` 指的是chaincode的源码。 186 | `metadata` 指的是与 chaincode 和 application相关的属性。 187 | `policies` **包含与所有peer可访问的Chaincode有关的policies(政策),例如endorsement policies(背书政策)**。 188 | **注意endorsement policies 不是部署 transaction中的txPayload提供的, `txPayload` 包含的是 endorsement policy ID 和它的参数(详见章节3)** 189 | 190 | * anchor 包含读取版本依赖关系,或更具体地说,key-version对(即,anchor是KxN的一个子集),它将`PROPOSE`请求绑定或“anchor”到KVS中指定版本的key(请参阅第1.2节)。 如果客户端指定anchor参数,则endorser仅在读取其本地KVS,并匹配anchor中的相应key的版本号时,才认可transaction(更多详细信息,请参见第2.2节)。 191 | 192 | tx的加密hash被所有节点用作唯一transaction标识符tid(即,`tid = HASH(tx)`)。 客户端将tid存储在内存中,并等待来自endorsing peers的响应。 193 | 194 | ##### 2.1.2. Message 模式 195 | 196 | client定与endorsers的交互顺序。 例如,client通常会发送``(即没有anchor参数)给一个单独的endorser,然后client可以生成版本依赖关系(anchor),以便client稍后可以将其用作PROPOSE消息的参数给其它endorser。 作为另一个例子,client可以直接发送``(没有anchor)给所选的所有endorser。 不同的通信模式都可以,client可以自由选择这些模式(另见2.3节)。 197 | 198 | ### 2.2. Endorsing peer 模拟一个 transaction(交易) 并且 产生一个endorsement 签名 199 | 200 | 在接收到来自客户端的``消息时,endorsing peer epID首先验证client的签名clientSig,然后模拟transaction。 如果client指定了anchor,则只有在,从其本地KVS中的对应key,读取到的版本号(即,下面定义的read set),匹配由anchor指定的版本号时,endorsing peer才模拟transaction。 201 | 202 | 通过调用Chaincode和endorsing peer本地的state,模拟一个transaction。 203 | 204 | 执行的结果是,endorsing peer计算出读取版本依赖(readset) 和 state 更新 (writeset)。 205 | 206 | 回想一下,state由key/value(k / v)对组成。 所有k / v条目都是版本化的。也就是说,每个条目都包含有序的版本信息,每次更新时存储在key下的version(版本)都会增加。 执行transaction的peer记录Chaincode访问的所有、读取或写入的k / v对。但peer尚未真正的更新其state。 进一步来说: 207 | 208 | * 在endorsing peer执行transaction之前,给定一个 state `s` ,对于transaction读的每一个key `k` , `(k,s(k).version)` 对被加到`readset`中。 209 | * 另外, 对于transaction的把每一个key `k`修改成新值 `v'` `(k,v')` 对被加到`writeset`中。 或者,`v'`可能是新值到先前值`(s(k).value)`的增量。 210 | 211 | 如果client在`PROPOSE`消息中指定了anchor,则client指定的anchor必须等于在模拟transaction时,由endorsing peer生成的`readset`。 212 | 213 | **``tran-proposal``是什么意思???** 214 | 然后,peer内部`tran-proposal`(转发proposal)(也可能是`tx`)到它endorses transaction的逻辑部分,称为endorsing逻辑。 默认情况下,peer的endorsing逻辑,接受`tran-proposal`,并简单对`tran-proposal`签名。 然而,endorsing逻辑可以按照功能任意定义的,例如使用`tran-proposal`和`tx`与 legacy systems交互, 并使用它们判断是否endorse a transaction(是否认可一个交易)。 215 | 216 | 如果endorsing逻辑决定endorse a transaction了(认可一个交易), 它发送 `` 消息 到 submitting client(`tx.clientID`): 217 | 218 | * `tran-proposal := (epID,tid,chaincodeID,txContentBlob,readset,writeset)`, 219 | 220 | `txContentBlob` 是 chaincode/transaction 特定信息。 目的是将 `txContentBlob` 用作 `tx` 的一些表示(例如 `txContentBlob=tx.txPayload`)。 221 | 222 | * `epSig` 是endorsing peer's 对`tran-proposal`的签名。 223 | 224 | 否则,如果endorsing逻辑拒绝endorse该事务,则endorser可以向submitting client发送消息(`TRANSACTION-INVALID,tid,REJECTED`)。请注意,在此步骤中endorser不会改变它的state。 225 | ### 2.3. Submitting client 收集一个transaction的 endorsement ,并通过ordering service把它广播出去 226 | Submitting client 等到它接受到足够的消息( "enough" messages) 和 statements的签名 (`TRANSACTION-ENDORSED, tid, *, *`) 才确定transaction proposal 是 endorsed(被认可的)。 像2.1.2节描述的那样, 这里可能调用一个或者多个 round-trips 和endorsers进行交互。 227 | 228 | “engouh”的确切数量取决于chaincode的 endorsement policy(另见第3节)。 如果endorsement policy 得到满足,transaction就得认可(同意或者批准); 229 | 230 | 请注意,它尚未提交。 从endorsing peers收集到的签名的`TRANSACTION-ENDORSED`消息,建立了由endorsement认可的transaction。 231 | 232 | 如果submitting client 没有设法收集transaction proposal的endorsement,它会放弃此transaction,并选择稍后重试。 233 | 234 | 对于有效认可的transaction,我们现在开始使用ordering service。 submitting client 使用广播(blob)调用ordering service,其中`blob =endorsement`(**这里什么意思???**)。 如果client没有直接调用ordering service的能力,它可能通过其选择的peer代理它的广播。 该peer必须被客户信任,它不会从endorsement中删除任何消息,否则transaction可能被视为无效。 但是请注意,代理peer可能不会编造有效的endorsement。 235 | 236 | ### 2.4. Ordering service发送 transactions到peers 237 | 238 | 当`deliver(seqno, prevhash, blob)`event 发生,并且一个peer已将所有状态更新应用于序列号低于seqno的blob时,peer执行以下操作: 239 | 240 | * 它根据chaincode的策略检查 ` blob.endorsement` 是否有效。(`blob.tran-proposal.chaincodeID`) 241 | * 通常情况下,它还验证依赖性(`blob.endorsement.tran-proposal.readset`)是否违反了(并发版本冲突检查)。 在更复杂的使用案例中,endorsement中的tran-proposal 字段可能有所不同,在这种情况下,endorsement policy(背书政策,第3部分)规定了state如何evolves(更新,发展)。 242 | 243 | 根据为状态更新,选择的一致性属性或“隔离保证”,可以用不同方式验证依赖关系。 除非chaincode 的 endorsement policy(背书策略)指定了不同的认证,否则可串行化是默认的隔离保证。 **可以通过要求与`readset`中的每个key相关联的版本,等于该key在state中的版本(也就是执行transaction前的数据和真正提交时候再次读取的数据,应该是一致的,这说明在这期间,没有其它的transaction修改这个值)**,以及拒绝不满足该要求的tranaction来提供可串行化性。 244 | 245 | * 如果所有这些检查都通过,transaction被视为有效或committed。 在这种情况下,peer在PeerLedger的bitmask(位掩码)中将transaction标记为1,将`blob.endorsement.tran-proposal.writeset`应用于区块链的state(如果`tran-proposal`是相同的,否则endorsement policy 逻辑定义了获取`blob.endorsement`的函数)。 246 | 247 | * 如果`blob.endorsement`的endorsement policy验证失败,则该transaction无效,并且该Peer在PeerLedger的bitmask(位掩码)中将transaction标记为0。 要注意无效transaction不会改变state。 248 | 249 | ![](media/transactionFlow.png) 250 | 251 | 图1. transaction 流程图 252 | 253 | 254 | # 3. Endorsement policies 255 | 256 | ## 3.1. Endorsement policy 规范 257 | Endorsement policy可以参数化,这些参数可以由部署transaction指定。 258 | ## 3.2. endorsement policy的Transaction 评估 259 | 对于部署transaction,根据系统范围的策略(例如,从system chaincode)获得endorsement(背书、认可)。 260 | 261 | endorsement policy指的是一些变量,可能下面几种: 262 | 263 | * keys或与Chaincode相关的身份(Chaincode的metadata元数据中可以找到),例如一组endorsers; 264 | * Chaincode的其它metadata数据; 265 | * `endorsement`的元素 和 `endorsement.tran-proposal`; 266 | 267 | * 其它可能的内容。 268 | 269 | 上面的列表是,是按照复杂性来排序的。也就是说,节点的keys和身份策略相对简单。 270 | 271 | **对endorsement policy的评估必须是确定性的。 endorsement应由每个peer在本地进行评估,以便peer不需要与其他peer交互,但所有正确的peer以同样的方式评估endorsement policy。(具体点什么意思呢?endorsement policy 不应该是client端去评估的么?)** 272 | 273 | ## 3.3 endorsement policies例子 274 | predicate(断言语句)可能包含逻辑表达式,根据表达式评估为TRUE或FALSE。 通常情况下,该条件将使用由Chaincode发布的transaction调用发出的数字签名(digital signatures)。 275 | 276 | 假设Chaincode指定endorser集合`E = {Alice,Bob,Charlie,Dave,Eve,Frank,George}`。 一些示例策略如下: 277 | 278 | * 来自E的所有成员的,同一份`tran-proposal`中的有效签名。 279 | 280 | * E.任何一个成员的有效签名。 281 | 282 | * 一组满足条件的peer的,同一个`tran-proposal`上的有效签名。 283 | 如条件:`(Alice OR Bob) AND (any two of: Charlie, Dave, Eve, Frank, George)`, 284 | * 7个endorsers中的任何5个,在同一个`tran-proposal`上的签名。 (更一般地,对于有`n> 3f` endorsers的Chaincode,`n`endorsers中的任何`2f + 1`的有效签名,或者超过`(n + f)/ 2`endorsers的任何群组。 285 | * 假设endorsers有“权重、(或者叫做股权)”,如`{Alice = 49,Bob = 15,Charlie = 15,Dave = 10,Eve = 7,Frank = 3,George = 1}` 总权重(股权)为100:该政策要求拥有大部分股权(权重)的组合(即,合并股权严格超过50的组合)的有效签名。 286 | 287 | * 前面示例条件中的股权(权重)的分配可以是静态的(固定在Chaincode的元数据中)或动态的(例如,取决于Chaincode的状态并且在执行期间可以被修改)。 288 | 289 | * 来自(Alice或Bob)的关于tran-proposal1的有效签名,以及来自(Charlie,Dave,Eve,Frank,George的任意两个)的关于tran-proposal2的有效签名。其中tran-proposal1和tran-proposal2仅在它们的endorsing peers 和state更新上不同。 290 | 291 | 这些政策的有用程度,取决于应用程序。解决方案针对endorsers的失败,或行为不当,的理想弹性(即处理方案)。 292 | 293 | # 4 (v1后续版本的新功能). Validated ledger and PeerLedger checkpointing (pruning) 294 | 现在版本的ledger中,有效和失效的transaction都存在。后续版本中将推出Validated ledger,仅存储有效的transaction。 295 | checkpointing功能将配合Validated ledger一起使用,帮忙替换掉无效的transaction,减少空间占用。 296 | 297 | 298 | 299 | 本文中有些名术语是基本等价的,像transactions和blobs,之所以没有统一为一个术语,是为了匹配上下文环境。文中有些疑问的地方,加了问好,明白了后会更新。 300 | 301 | 欢迎加入区块链技术交流QQ群 694125199 302 | 303 | 本文主要根据官方架构文档,翻译而来 304 | https://github.com/hyperledger/fabric/blob/release-1.1/proposals/r1/Next-Consensus-Architecture-Proposal.md 305 | 306 | 本文地址: 307 | https://github.com/xiaofateng/knowledge-without-end/blob/master/区块链/Hyperledger/Hyperledger%20Fabric%201.0架构介绍.md 308 | 309 | 310 | -------------------------------------------------------------------------------- /Hyperledger/Hyperledger Fabric MSP和Fabric CA的区别.md: -------------------------------------------------------------------------------- 1 | # Hyperledger Fabric MSP和Fabric CA的区别 2 | 3 | MSP是Membership Service Provider - 是可插拔的接口,它用于支持各种认证体系结构,为membership orchestration architecture提供抽象层。 MSP抽象提供: 4 | 5 | * 具体的身份格式 6 | * 用户证书验证 7 | * 用户证书撤销 8 | * 签名生成和验证 9 | 10 | 而 Fabric-CA 用于生成证书和密钥,以真正的初始化MSP。 Fabric-CA是用于身份管理的MSP接口的默认实现。 11 | 12 | **即,MSP只是一个接口,Fabric-CA是MSP接口的一种实现。** 13 | 14 | 15 | 在官方的例子中,BYFN, 用命令`./byfn.sh -m up` 启动网络。 16 | 这时候网络中是没有CA containers的,那么MSP是怎么工作的呢? 17 | 18 | 在构建第一个网络示例时,使用了主要用于测试和演示场景的`cryptogen`工具,以快速设置初始化MSP所需的加密资料,基本上它会生成操作网络实体所需的certificates(证书)。 因此,实际上并不需要使用Fabric-CA。 19 | 20 | 使用Fabric-CA的示例在这里: 21 | https://github.com/hyperledger/fabric-samples/tree/master/fabric-ca 22 | 23 | 24 | 欢迎加入区块链技术交流QQ群 694125199 25 | 26 | 更多区块链知识: 27 | https://github.com/xiaofateng/knowledge-without-end/tree/master/区块链/Hyperledger 28 | 29 | 30 | -------------------------------------------------------------------------------- /Hyperledger/Hyperledger Fabric 源码和镜像下载.md: -------------------------------------------------------------------------------- 1 | # Hyperledger Fabric 源码和镜像下载 2 | 在这之前需要下载安装Docker和Go环境,安装简单,不再介绍。 3 | 4 | Hyperledger Fabric的源码和镜像下载非常简单,只需要执行下面一个命令: 5 | 6 | ` curl -sSL https://goo.gl/6wtTN5 | bash -s 1.1.0-rc1 7 | ` 8 | 9 | 上面的命令,是执行下面的脚本 10 | https://github.com/hyperledger/fabric/blob/master/scripts/bootstrap.sh 11 | 12 | 上面脚本里面的内容就是下载Fabric源码: 13 | - cryptogen 14 | - configtxgen 15 | - configtxlator 16 | - peer 17 | - orderer 18 | - fabric-ca-client 19 | 上面的代码,将放到当前目录的bin目录下面。 20 | 21 | 并且下载下面的镜像: 22 | - hyperledger/fabric-ca latest 8a6c8c2e2ebf 13 days ago 283MB 23 | - hyperledger/fabric-ca x86_64-1.1.0-rc1 8a6c8c2e2ebf 13 days ago 283MB 24 | - hyperledger/fabric-tools latest 006c689ec08e 13 days ago 1.46GB 25 | - hyperledger/fabric-tools x86_64-1.1.0-rc1 006c689ec08e 13 days ago 1.46GB 26 | - hyperledger/fabric-orderer latest 10afc128d402 13 days ago 180MB 27 | - hyperledger/fabric-orderer x86_64-1.1.0-rc1 10afc128d402 13 days ago 180MB 28 | - hyperledger/fabric-peer latest 6b44b1d021cb 13 days ago 187MB 29 | - hyperledger/fabric-peer x86_64-1.1.0-rc1 6b44b1d021cb 13 days ago 187MB 30 | - hyperledger/fabric-javaenv latest ea263125afb1 13 days ago 1.52GB 31 | - hyperledger/fabric-javaenv x86_64-1.1.0-rc1 ea263125afb1 13 days ago 1.52GB 32 | - hyperledger/fabric-ccenv latest 65c951b9681f 13 days ago 1.39GB 33 | - hyperledger/fabric-ccenv x86_64-1.1.0-rc1 65c951b9681f 13 days ago 1.39GB 34 | - hyperledger/fabric-zookeeper latest 92cbb952b6f8 3 weeks ago 1.39GB 35 | - hyperledger/fabric-zookeeper x86_64-0.4.6 92cbb952b6f8 3 weeks ago 1.39GB 36 | - hyperledger/fa。bric-kafka latest 554c591b86a8 3 weeks ago 1.4GB 37 | - hyperledger/fabric-kafka x86_64-0.4.6 554c591b86a8 3 weeks ago 1.4GB 38 | - hyperledger/fabric-couchdb latest 7e73c828fc5b 3 weeks ago 1.56GB 39 | - hyperledger/fabric-couchdb x86_64-0.4.6 7e73c828fc5b 3 weeks ago 1.56GB 40 | 41 | 由上面的列表可以看出,会下载Fabric所依赖的Zookeeper、Kafka、Couchdb等docker镜像。并且镜像文件还挺大。 42 | 43 | 最后可以把 44 | ` export PATH=/bin:$PATH ` 45 | 加入到环境变量。 46 | 47 | **会遇到最大的问题是,国内连接国外的网络,非常慢,会导致各种问题,例如下载到一半时候网络超时,下载失败等等。最好、最直接的方式是VPN下安装,科学上网的方法推荐使用外国VPN,稳定一些。** 48 | 49 | **只要解决了网络问题,本文中的步骤非常简单,而且不会出现各种问题** 50 | 51 | 更多内容 https://github.com/xiaofateng/knowledge-without-end 52 | 53 | -------------------------------------------------------------------------------- /Hyperledger/Hyperledger Fabric中的 事件框架.md: -------------------------------------------------------------------------------- 1 | # 介绍 2 | 事件框架支持发出2种类型的event(事件),`block`和`自定义/chaincode event`(在`events.proto`中定义的`ChaincodeEvent`类型)的能力。基本思想是,client(event consumers\事件消费者)将注册event类型(当前为`“block”`或`“chaincode”`)。并且在chaincode的情况下,它们可以指定附加的注册标准,即`chaincodeID`和`eventname`。 3 | 4 | `ChaincodeID`标识client想要查看event的特定Chaincode。 5 | `eventname`是Chaincode开发人员,在调用Chaincode中的`SetEvent API`时嵌入的字符串。调用transaction是当前唯一可以发出event的操作,并且每个调用,在每个transaction中只能发出一个event。 6 | 7 | # 设计: 使用 TransactionResult 存储 events 8 | 向`TransactionResult`加入一个event消息 9 | 10 | ``` c 11 | / chaincodeEvent - any event emitted by a transaction 12 | type TransactionResult struct { 13 | Uuid string 14 | Result []byte 15 | ErrorCode uint32 16 | Error string 17 | ChaincodeEvent *ChaincodeEvent 18 | } 19 | ``` 20 | ChaincodeEvent是: 21 | 22 | ``` c 23 | type ChaincodeEvent struct { 24 | ChaincodeID string 25 | TxID string 26 | EventName string 27 | Payload []byte 28 | } 29 | ``` 30 | 31 | `ChaincodeID`是与Chaincode关联的`uuid`,`TxID`是生成event的事transaction,`EventName`是由Chaincode给event赋予的名称(请参阅SetEvent),`Payload`是Chaincode附加到event的payload(有效载荷)。 Fabric不对有效载荷施加struct或限制,但最好不要使其尺寸太大。**Payload具体是什么?** 32 | 33 | chaincode Shim上的 `SetEvent API` 34 | 35 | ```c 36 | func (stub *ChaincodeStub) GetState(key string) 37 | func (stub *ChaincodeStub) PutState(key string, value [\]byte) 38 | func (stub *ChaincodeStub) DelState(key string) 39 | … 40 | … 41 | func (stub *ChaincodeStub) SetEvent(name string, payload []byte) 42 | … 43 | … 44 | ``` 45 | 46 | 当transaction完成时,`SetEvent`将event添加到`TransactionResult`并提交到ledger。 然后该event将由event framework(事件框架)发布到,注册该event的所有客户端。 47 | 48 | ## 一般Event类型及其与Chaincode Event的关系 49 | 50 | Event与event类型相关联。 客户注册他们想要接收event的event类型。 event类型的生命周期由`“block”`event来说明 51 | 52 | 1.在启动peer时,在支持的event类型中添加`“block”` 53 | 2.client可以与peer(或多个peers)一起注册感兴趣的`“block”` event类型 54 | 3.创建`Block`的Peers,向所有注册client发布event 55 | 4.客户收到`“block”` event并处理`Block`中的事务 56 | 57 | Chaincode event添加了额外的注册过滤级别。 Chaincode event不是注册给定event类型的所有event,而是允许client从特定Chaincode注册特定event。 对于目前的第一个版本,为了简单起见,没有在`eventname`上实现通配符或正则表达式匹配,但后续会做。 更多信息,参见下面的`“Client Interface”`。 58 | 59 | ## Client Interface 60 | 61 | client 当前注册并接收event的接口是一个gRPC接口,带有在`protos / events.proto`中定义的`pb`消息。 `Register` message 是一系列(重复的) `Interest` messages。每个 `Interest` 代表一个单独的event注册. 62 | 63 | ```c 64 | message Interest { 65 | EventType eventType = 1; 66 | //理想的情况下,对不同的Reg types ,我们应该只有下面的 oneof 并摆脱 EventType。 67 | //但它是这样的 API: 68 | //改变 Additional Reg 类型,可能增加特定类型的 69 | //messages 添加到the oneof 70 | oneof RegInfo { 71 | ChaincodeReg chaincodeRegInfo = 2; 72 | } 73 | } 74 | 75 | //当 EventType 是 CHAINCODE 时候, 76 | //ChaincodeReg 用来注册 chaincode Interests 77 | message ChaincodeReg { 78 | string chaincodeID = 1; 79 | string eventName = 2; 80 | ~~bool anyTransaction = 3;~~ //TO BE REMOVED 81 | } 82 | 83 | //consumers发出 Register 来注册events 84 | //string type - "register" 85 | message Register { 86 | repeated Interest events = 1; 87 | } 88 | 89 | ``` 90 | 91 | 如前面部分所述,client应该使用`ChaincodeReg`消息注册Chaincode event。 使用空字符串(或`“*”`)设置`eventName`将导致chaincode中的所有event发送到listener。 92 | 93 | 在`events.proto`中定义了一种服务,使用单一方法接收注册event的stream(流),并返回client,当 event 发生时读取事件的stream(流)。 94 | 95 | 欢迎加入区块链技术交流QQ群 694125199 96 | 97 | 更多区块链知识: 98 | https://github.com/xiaofateng/knowledge-without-end/tree/master/区块链/Hyperledger 99 | 100 | 本文参考 101 | https://github.com/hyperledger/fabric/blob/release-1.1/proposals/r1/Custom-Events-High-level-specification.md 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /Hyperledger/Hyperledger基础介绍.md: -------------------------------------------------------------------------------- 1 | # Hyperledger基础介绍 2 | 包含分布式账本和智能合约。 3 | ## 什么是分布式账本 4 | 一个只允许记录或者交易日志追加的系统。 5 | Blockchain (区块链) 可以用来记录承诺、交易或者简单的我们不想让它消失的条目。 6 | 在给定网络的所有节点中,它都存在备份,不可能被擦除或者修改。 7 | (基于共识机制,擦除或者修改,意味着要修改所有节点中至少一半节点的内容,作弊成本非常高,几乎不可能。) 8 | 交易按照区块链中的顺序处理,处理的结果就是一个分布式账本。 9 | ## 智能合约 10 | 是一段代码或者复杂的程序,它们存储在区块链上,并且在区块链上执行。这个代码会在不同的节点上同时执行。 11 | ### 智能合约的运用举例: 12 | 假设一个农场主,为他的农作物买了保险,如果极端天气的气温超过50度,并且天数超过了100天,那么保险公司要赔偿100万。 13 | 现在,这样的合约是线下签订,当事情发生的时候,保险公司可能会找各种理由拒绝赔偿。 14 | 当使用智能合约的时候,极端天气发生后,100万会自动打入农场主的账号,这是代码自动执行的,而且是不可撤回的。 15 | ## 区块链不是加密货币 16 | Cryptocurrency(cryptography + currency的组合),即加密货币,是比特币、以太币这样的。而区块链不等同于这些。加密货币仅仅是基于区块链的一个应用。 17 | 18 | ## 为什么使用商业区块链 19 | 全球的商场都有账本,组织和个人一样,都必须相信账本。 20 | 大多数企业区块链应用都是基于,真实世界的信任关系。它的目标是建立一个生态系统,使更多的可信的合作伙伴加入进来。 21 | 在一个被限制的网络中,参与者彼此了解,他们很有兴趣参与相同的活动。不需要POW(工作量证明),他们能够解决更即时的问题。 22 | ## 商业区块链组件术语 23 | * Consensus Layer,在交易的顺序和正确性上达成共识 24 | * Smart Contract Layer,处理交易请求,并且通过执行商业逻辑代码来判断这个交易是否有效。 25 | * Communication Layer,负责节点间peer to peer的消息传输 26 | * Data Store Abstraction,允许其它的模块使用不同的数据库存储 27 | * Crypto Abstraction,允许不同的加密算法或者模块相互替换,而不影响其它的模块。 28 | * Identity Services,在启动区块链实例的时候,保证建立一个最根本的信任。登录或者注册时候的身份验证,网络操作时候的身份验证。同时,进行对身份的管理,包括删除、增加、更改等。提供身份的验证和授权。 29 | * Policy Services, 系统中各种各样的策略的管理。例如,背书策略、共识策略、分组管理策略。它和其它的模块进行交互,并且是依赖其它的模块来强制执行这些策略。 30 | * APIs,使客户端和程序能够和区块链进行交互。 31 | * Interoperation,支持不同区块链实例间的互操作。 32 | 33 | ## Hyperledger模块化设计 34 | Hyperledger像Linux一样,是模块化设计。 35 | 36 | ![](media/15210198930172/15210201011823.jpg) 37 | 38 | ## Hyperledger区块链框架 39 | * Hyperledger Fabric,作为开发程序,或者解决模块架构问题的基础架构。它允许组件,像共识服务、成员服务是可插拔的。 40 | * Hyperledger Iroha,一个商业的区块链框架,可以简便的嵌入到需要用到分布式账本技术的项目中。 41 | * Hyperledger Sawtooth,一个构建、部署、运行分布式账本的平台。它包含一个共识算法,Proof of Elapsed Time (PoET),它的目标是,大量的分布式验证人群,使用更少的资源消耗。 42 | * Hyperledger Burrow,一个许可的智能合约机制。 43 | * Hyperledger Indy,提供区块链上数字一致性的工具、库、可重用的组件,这样就可以在管理区域、程序、或者其它的仓库之间进行互操作。 44 | 45 | ## 使用场景 46 | 跨国支付,健康信息记录,食物安全供应链,钻石供应链,难民身份ID,真正的房地产交易,音乐媒体产权,绿色能源管理(中国二氧化碳排放量) 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /Hyperledger/Identity介绍.md: -------------------------------------------------------------------------------- 1 | # 什么是Identity? 2 | 区块链网络中的不同参与者包括peer,orderers,client applications,administrators等等。 3 | 这些参与者中的每一个,都有封装在X.509数字证书中的Identity(身份)。**这些Identity真的很重要,因为它们确定了参与者,在区块链网络中拥有的资源权限。** 4 | Hyperledger Fabric使用参与者identity(身份)中的某些属性来确定权限,并为其提供特殊名称 - principal(主体)。principal就像用户ID或组ID一样,但更灵活一点,因为它们可以包含广泛的参与者的identity属性。 5 | 参与者identity的属性决定了它们的权限。这些属性通常是,参与者的organization,organizational unit,角色或参与者的特定identity。 6 | 7 | 最重要的是,identity必须是可验证的,因此它必须来自系统所信任的权威机构。MSP是在Hyperledger Fabric中实现此目标的手段。更具体地说,MSP是代表组织成员资格规则的组件,因此它定义了,管理org成员有效identity(身份)的规则。 Fabric中默认的MSP实现使用X.509证书作为identity,采用PKI分层模型。 8 | 9 | **MSP把验证过的identities 转换成区块链网络中的members** 10 | 11 | 关于Identity的举例: 12 | 例如你去超市购物,结账的时候,只允许visa卡支付,尽管你的smart卡里面有钱,但是不能支付。 13 | PKI提供identies的列表,MSP说出来,哪一个,是参与区块链网络的org的成员。 14 | 15 | -------------------------------------------------------------------------------- /Hyperledger/MSP图解.md: -------------------------------------------------------------------------------- 1 | # 2 | 图1 3 | 4 | ![](media/MSPOrgRelation.png) 5 | 6 | 7 | 8 | 图2 9 | 10 | ![](media/localAndchannelMSPS.png) 11 | 12 | 13 | 图3 14 | 15 | ![](media/MSPElemment.png) 16 | 17 | 18 | 图4 19 | 20 | ![](media/MSPxmind.png) 21 | 22 | 23 | 24 | 图5 25 | ![](media/MSPxmind2.png) 26 | 27 | 在order setup的时候,需要生成并向orderer提供system Channel的genesis block。 28 | 29 | 出现在网络中所有MSP的验证元素(即各种证书,配置),必须包含到系统channel的genesis block中 30 | 31 | 管理该channel的MSP,必须存在于channel的创世纪块中 32 | 33 | -------------------------------------------------------------------------------- /Hyperledger/MSP详细介绍.md: -------------------------------------------------------------------------------- 1 | # 简介 2 | 3 | MSP的作用,不仅仅在于列出谁是网络参与者或Channel成员。 MSP可以确定,成员在MSP所代表的Org(trust domain)(例如,MSP管理员,组织细分成员)中扮演的特定角色。 它将MSP的配置通告给,相应组织的成员参与的所有Channel(以MSP Channel的形式)。 Peers, orderers 和 clients还维护本地MSP实例(也称为 Local MSP),以在channel环境之外验证其组织成员的消息。 此外,MSP可以识别已被吊销的身份列表。 4 | 也就是MSP可以分为:local 和 channel MSPs 5 | 6 | 对于MSP的简单理解: 7 | 提供identity(身份)到(organization)组织的映射 - 它确定如何将peer分配给特定组织中的特定角色,并因此获得对区块链资源的适当访问。 8 | 9 | **或者另外一种理解: 10 | MSP的任务是 11 | 验证签名者的身份和签名者在该MSP内的角色。 12 | 目前,支持两个角色:成员和管理员。** 13 | 14 | 签名者角色,有四个,member、admin、client、peer 15 | 16 | **organizations 最重要的是,他们在单独的一个MSP下管理其成员。** 请注意,这与在X.509证书中定义的organization概念不同,我们稍后会讨论。 17 | 18 | 19 | organization与MSP之间的专一性关系,使得在organization之后命名MSP是明智的,在大多数policy配置中,您会发现这一个惯例。 例如,organization `ORG1`将有一个名为 `ORG1-MSP` 的MSP。 在某些情况下,organization可能需要多个成员资格组 - 例如,不同的Channel用于在organisations之间执行完全不同的业务职能, 在这些情况下,拥有多个MSP并相应地命名它们是有意义的,例如`ORG2-MSP-NATIONAL` 和 `ORG2-MSP-GOVERNMENT`,反映了与政府监管Channel相比,国家销售Channel中ORG2中不同的成员信任root。详细见下图例子: 20 | 21 | ![](media/MSPOrgRelation.png) 22 | 23 | 第一种配置显示了MSP和Org之间的典型关系 - 单个MSP定义Org成员的列表。 24 | 在第二种配置中,不同的MSP用于代表具有国家,国际和政府关系的不同组织团体。 25 | 26 | # Organizational Units(企业中各管理部门) and MSPs 27 | 28 | 一个组织经常被分成多个组织单位(organizational units 、OUs),每个组织单位都有一定的责任。 例如,`ORG1`组织可能同时具有`ORG1-MANUFACTURING`和`ORG1-DISTRIBUTION` OUS,以反映这些单独的业务线。 当CA颁发X.509证书时,证书中的`OU`字段将指定该identity(身份)所属的业务线。 29 | 30 | 我们稍后会看到`OUs`如何有助于控制,区块链网络成员的组织部分。 例如,只有来自`ORG1-MANUFACTURING` OU的身份可能能够访问某个Channel,而`ORG1-DISTRIBUTION`则不能。 31 | 32 | # Local and Channel MSPs 33 | 34 | MSP出现在区块链网络中的两个地方:Channel配置(Channel MSP),以及本地(local MSP)。localMSP,是为节点(peer 或 orderer)和用户(使用CLI或使用SDK的客户端应用程序的管理员)定义的。**每个节点和用户都必须定义一个localMSP,因为它定义了谁在该级别和Channel的上下文之外,具有管理或参与权限(例如,谁是peer所在组织的管理员)。** 35 | 36 | 相反,channel MSP在channel层面定义管理和参与权。参与Channel的每个组织,都必须为其定义MSP。Channel上的Peers 和 orderers将在Channel MSP上共享相同的视图,并且此后将能够正确认证Channel参与者。**这意味着如果一个组织希望加入该Channel,那么需要在Channel配置中,加入一个包含该组织成员的信任链的MSP。否则来自该组织身份的交易将被拒绝。** 37 | 38 | local和channel MSP之间的主要区别不在于它们的功能,而在于它们的范围。 39 | 40 | ![](media/localAndchannelMSPS.png) 41 | 42 | 43 | Local 和 channel MSP。 **每个peer的信任域(例如,组织)由peer 的Local MSP(例如`ORG1`或`ORG2`)定义。** 组织在Channel中的表示,通过将组织的MSP包含在渠道中来实现。 例如,上图的Channel由`ORG1`和`ORG2`管理。 类似的原则适用于网络,orderers和用户,但为简单起见,这里没有显示。 44 | 45 | 46 | Local MSP仅在其应用的 节点或用户 的文件系统上定义。 因此,在物理上和逻辑上,每个节点或用户只有一个Local MSP。 47 | 48 | 但是,由于Channel MSP可用于Channel中的所有节点,因此它们在其配置中的Channel中逻辑定义一次。 **但是,Channel MSP在channel中的每个节点的文件系统上,实例化并且通过共识保持同步。** 因此,虽然每个节点的本地文件系统上存在每个channel MSP的副本,但逻辑上,channel MSP驻留在channel或网络上并由其维护。 49 | 50 | 上图中,管理员B使用`RCA1`颁发的,并存储在其local MSP中的身份连接到peer(用户、管理员也有local MSP)。 当B尝试在peer上安装智能合同时,peer检查其local MSP `ORG1-MSP`,以验证B的身份确实是`ORG1`的成员。 成功验证将允许安装命令成功完成。 随后,B希望实例化该channel上的智能合约。 由于这是Channel操作,Channel中的所有组织都必须同意。 因此,peer必须先检查Channel的MSP,然后才能成功提交该命令。 (其他事情也必须发生,但现在要专注于上述内容。) 51 | 52 | 53 | # MSP Levels 54 | 55 | MSP级别 56 | channel 和 local MSP之间的分割反映了,组织管理其本地资源(例如peer or orderer节点)及其Channel资源(例如在Channel或网络级别,运营的ledgers,智能合约和联盟)的需求。 将这些MSP视为处于不同级别是有好处的,其中较高级别的MSP,处理与网络管理有关的问题,而较低级别的MSP,处理私有资源管理的身份。 MSP在每个管理级别都是必须的 - 它们必须为网络,Channel,peer, orderer 和 users 定义。 57 | 58 | 59 | ![](media/MSPLevel.png) 60 | 61 | peer 和 orderer的MSP是本地的,而Channel的MSP **(包括网络配置Channel)** 在该Channel的所有参与者之间共享。 在上图中,网络配置Channel由ORG1管理,但另一个应用程序Channel可由ORG1和ORG2管理。 62 | 63 | peer是ORG2的成员,并由ORG2管理,而ORG1管理orderer。 ORG1信任来自RCA1的身份,而ORG2信任来自RCA2的身份。 请注意,这些是管理身份,反映了谁可以管理这些组件。 所以当ORG1管理网络时,ORG2.MSP确实存在于网络定义中。 64 | 65 | 66 | * Network MSP:网络配置通过定义参与者组织MSPs,来定义网络中的成员。同时定义这些成员中哪些成员,有权执行管理任务(例如,创建Channel) 67 | 68 | 69 | * Channel MSP:Channel分开维护其成员的MSP非常重要。Channel提供了一组特定的组织之间的私人通信,这些组织又对其进行管理控制。 **在该Channel的MSP上下文中的Channel policies定义谁能够参与Channel上的某些操作,例如添加组织或实例化chaincodes。** 请注意,管理Channel的权限与管理网络配置Channel(或任何其他频道)的权限之间没有必要的关系。管理权限存在于正在管理的范围内(除非规则已另行编写 - 请参阅下面关于ROLE属性的讨论)。 70 | 71 | 72 | * Peer MSP:此Local MSP在每个peer的文件系统上定义,并且每个peer都有一个MSP实例。从概念上讲,它执行的功能与Channel MSP完全相同,限制条件是它仅用于定义它的peer上。 73 | 74 | 75 | * Orderer MSP:与peer MSP一样,orderer local MSP也在节点的文件系统上定义,并且仅用于该节点。与peer 节点相似,orderer也由单个组织拥有,因此只有一个MSP来列出其信任的参与者或节点。 76 | 77 | # MSP Structure 78 | 79 | 到目前为止,已经看到MSP中最重要的两个要素,是用于确定相应组织中的参与者或节点成员资格的(root or intermediate)CA的规范。 但是,有更多的元素与这两个元素结合使用来协助membership功能。 80 | 81 | ![](media/MSPElemment.png) 82 | 83 | 下面详细介绍下: 84 | 85 | ## Root CAs 86 | 此文件夹包含,由此MSP代表的组织信任的Root CA的,自签名X.509证书列表。此MSP文件夹中必须至少有一个Root CA X.509证书。 87 | 这是最重要的文件夹,因为它标识了所有其它证书,必须从中派生出来的CA,以便被视为相应组织的成员。 88 | 89 | ## Intermediate CAs 90 | 此文件夹包含此组织信任的Intermediate CA的X.509证书列表。每个证书都必须由MSP中的一个Root CA签署,或者由 Intermediate CA 签署。 91 | 92 | Intermediate CA可以表示组织的不同细分或组织本身(例如,如果商业CA用于组织的身份管理)。在后一种情况下,可以使用CA层次结构中,较低的其他Intermediate CA来表示组织细分。请注意,可能有一个没有任何中间CA的功能网络,在这种情况下,此文件夹将为空。 93 | 94 | 与Root CA文件夹一样,此文件夹定义了,必须从中颁发证书才能被视为组织成员的CA。 95 | 96 | ## Organizational Units (OUs) 97 | 可选的 98 | 99 | ## Administrators 100 | 该文件夹包含一个身份列表,用于定义具有该组织管理员角色的参与者。对于标准MSP类型,此列表中应该有一个或多个X.509证书。 101 | 102 | 值得注意的是,仅仅因为演员具有管理员的角色,并不意味着他们可以管理特定的资源!给定身份在管理系统方面的实际权力,取决于管理系统资源的策略。例如,Channel policy可能会指定`ORG1-MANUFACTURING`管理员有权将新组织添加到Channel,而`ORG1-DISTRIBUTION`管理员则没有此权限。 103 | 104 | **即使X.509证书具有ROLE属性(例如,指定角色是管理员),这也是指角色在其组织中而不是区块链网络中的角色。**这与OU属性的用途类似 - 指的是参与者在组织中的位置。 105 | 106 | 如果该Channel的策略已经写入,允许来自组织(或某些组织)的任何管理员,执行某些channel功能(例如实例化chaincode)的权限,则ROLE属性,可用于在channel级别授予管理权限。**通过这种方式,组织角色可以赋予网络角色。这在概念上类似于美国佛罗里达州颁发的驾驶执照,是如何授权某人在美国的每个州开车的。** 107 | 108 | 109 | ## Revoked Certificates: 110 | 可选的 111 | 112 | ## Node Identity(节点身份) 113 | **该文件夹包含节点的自身身份。与KeyStore内容相结合的密码材料,将允许节点,在发送给其channels和network的其他参与者的消息中,对自身进行身份验证。** 对于基于X.509的身份,此文件夹包含一个X.509证书。 例如,这是peer在交易提议响应中的证书,以表明peer已经认可了该证书 - 随后可以在验证时,根据所得到的交易的认可政策,来检查该证书。 114 | 115 | 该文件夹对于本地MSP是必需的,并且该节点必须只有一个X.509证书。 它不用于Channel MSP。 116 | 117 | 118 | ## KeyStore for Private Key(私钥KeyStore) 119 | 该文件夹为peer 或 orderer节点(或客户端的local MSP)的local MSP定义,并包含节点的signing key(签名密钥)。 **此密钥,与上面Node Identity 文件夹中的节点身份匹配,并用于签署数据 - 例如签署交易提议响应,作为认可阶段的一部分。** 120 | 121 | 该文件夹对Local MSP是必须的,并且必须包含一个私钥。 很明显,访问这个文件夹,只能由,对次peer有管理权限的用户。 122 | 123 | Channel MSP的配置不包括此部分,因为Channel MSP旨在提供纯粹的身份验证功能,而不是签署能力。 124 | 125 | 126 | ## TLS Root CA 127 | 此文件夹包含,此组织为TLS通信所信任的Root CA的自签名X.509证书列表。 **TLS通信的一个例子是,peer需要连接到orderer以便它可以接收ledger更新。** 128 | 129 | MSP TLS信息涉及网络内的节点,即对peers 和 the orderers,而不是那些使用网络的节点 - 应用程序和管理员。 130 | 131 | 此文件夹中必须至少有一个TLS Root CA X.509证书。 132 | 133 | ## TLS Intermediate CA 134 | 此文件夹包含由此MSP代表的,组织信任的用于TLS通信的Intermediate CA证书列表。当商业CA用于组织的TLS证书时,此文件夹特别有用。 它是可选的。 135 | 136 | -------------------------------------------------------------------------------- /Hyperledger/Peer channel-based event services .md: -------------------------------------------------------------------------------- 1 | # 简介 2 | 3 | 在以前的Fabric版本中,peer event service 被称为event hub。 无论block关联哪个Channel,该服务都会在任何时候,将新block添加到peer Ledger时发送event。并且只有运行event peer的组织的成员才可以访问该event。 4 | 5 | 6 | 从v1.1开始,有两个提供event的新服务。 这些服务使用完全不同的设计来按每个Channel提供事件。 这意味着event的注册发生在channel的层面而不是peer层面,允许对peer数据的访问进行细粒度上的控制。 请求接受来自peer组织外部的身份的event(由Channel配置定义)。 这也提供了更高的可靠性和接收可能错过的event(无论是由于连接问题,还是由于peer正在加入已经运行的网络)。 7 | 8 | 9 | # Available services 10 | * `Deliver` 11 | 12 | 该服务发送已提交到Ledger的整个block。 如果event由Chaincode设置,则可以在block的`ChaincodeActionPayload`中找到这些event。 13 | 14 | * `DeliverFiltered` 15 | 16 | 该服务发送“filtered” block,以及有关已提交到ledger的块的最小信息集。 它旨在用于网络中,peer的所有者,希望外部client主要接收有关其transaction和这些transaction状态的信息。 如果任何event由Chaincode设置,则可以在filtered block的`FilteredChaincodeAction`中找到这些event。 17 | 18 | # 怎样注册事件(register for events) 19 | 20 | 通过向peer发送,包含deliver seek info message的信封,来完成来自任一服务的事件的注册。该信封包含期望的开始和停止位置,查找行为(如果未准备就绪,则阻塞直到就绪或失败)。 有帮助变量`SeekOldest`和`SeekNewest`可用于指示ledger中最早的(即第一个)块或最新的(即最后一个)块。 要使服务无限期地发送event,`SeekInfo`消息应该包含一个停止位置的`MAXINT64`。 21 | 22 | 默认情况下,这两个服务都使用Channel读取器策略,来确定是否为请求客户端授权event。 23 | 24 | # deliver response 消息概览 25 | 26 | event services发回`DeliverResponse`消息。 27 | 28 | 每个消息都包含以下内容之一: 29 | 30 | * status - HTTP状态码。 如果发生任何故障,两种服务都将返回相应的故障代码; 否则,一旦服务完成发送`SeekInfo`消息请求的所有信息,它将返回`200` - SUCCESS。 31 | * block - 仅由`Deliver` service返回。 32 | * filtered block - 仅由`DeliverFiltered`service返回。 33 | 34 | 一个filtered block 包含: 35 | 36 | * channel ID. 37 | 38 | * number (如 block number) 39 | 40 | * filtered transactions数组 41 | 42 | * transaction ID 43 | type (如`ENDORSER_TRANSACTION`, `CONFIG`) 44 | transaction validation code. 45 | 46 | * filtered transaction actions 47 | filtered chaincode actions数组。 48 | transaction的chaincode event。 49 | 50 | 51 | 欢迎加入区块链技术交流QQ群 694125199 52 | 53 | 更多区块链知识: 54 | https://github.com/xiaofateng/knowledge-without-end/tree/master/区块链/Hyperledger 55 | 56 | 本文参考: 57 | http://hyperledger-fabric.readthedocs.io/en/latest/peer_event_services.html 58 | 59 | 60 | -------------------------------------------------------------------------------- /Hyperledger/Read-Write set语义实现细节.md: -------------------------------------------------------------------------------- 1 | # Read-Write set语义 2 | 本文讨论了关于Read-Write set语义的当前实现的细节。 3 | 4 | 5 | 6 | ## Transaction 模拟和 read-write set 7 | 8 | **在endorser上模拟transaction时,为transaction准备了read-write set**。 `read set`包含在模拟期间,transaction读取的唯一keys及其提交版本(version)的列表。 `write set`包含唯一keys列表(尽管可能与read set中的keys存在重叠)以及transaction写入的新值。 如果transaction执行的更新是删除key,则为该key设置delete marker(删除标记)(代替新值)。 9 | 10 | 另外,如果transaction为一个key多次写入一个值,则只保留最后一次写入的值。 此外,如果transaction读取某个key的值,则即使transaction在发出读取之前就更新了key的值,也会返回committed state的值(即,读取的是ledger中的值,而不是模拟执行的结果)。 换句话说,不支持Read-your-writes语义。 11 | 12 | 如前所述,key的版本只记录在read set中; write set 只包含由transaction设置的唯一key列表及其最新值。 13 | 14 | 可能有各种实现版本(version)的方案。版本控制方案的最低要求是为给定key生成非重复标识符。例如,对版本(version)使用单调递增数字的方案。 15 | 16 | 在当前实现中,我们使用基于blockchain(区块链)高度的版本控制方案,其中提交transaction的高度,用作transaction修改的所有key的最新版本。在这种方案中,transaction的高度由一个元组表示(`txNumber` 是块内transaction的高度)。与增量数字方案相比,此方案具有许多优点 - 主要是,它可以实现其他组件,如statedb,transaction模拟和验证,以便进行高效的设计选择。 17 | 18 | 以下是通过模拟假设transaction,准备的`read-write set`的说明。为了简单起见,在插图中,我们使用增量数字来表示版本。 19 | 20 | ```c 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 32 | 33 | 34 | ``` 35 | 36 | 此外,如果transaction在模拟过程中执行范围查询,范围查询及其结果将作为query-info添加到read-write set中。 37 | 38 | ## Transaction 有效性检查 和 使用read-write set 更新 world state 39 | 40 | committer使用read-write set中的`read set`部分来检查transaction的有效性,并使用`write set`部分来更新受影响的key的版本和值。 41 | 42 | 在验证阶段,如果transaction的 `read set` 中存在的每个key的版本与世界状态(world state)下的相同key的版本相匹配,则认为该transaction有效 - **假定所有先前的有效transaction(包括相同 block 中前述的transaction)都被提交了(提交状态)**。 如果read-write set还包含一个或多个query-info,则执行额外的验证。 43 | 44 | 这种额外的验证,应确保在query-info(s)中获取的结果的超范围(super range,即,范围的联合)中没有key被插入/删除/更新。换句话说,如果我们在committed-state验证期间,重新执行任何范围查询(模拟期间执行的transaction),它应该产生与模拟transaction观察到的结果相同的结果。该检查可确保,如果transaction在提交期间观察到幻象结果(假的,不符合的结果),则该transaction应被标记为无效。 45 | 46 | 请注意,该幻象保护仅限于范围查询(即,Chaincode中的`GetStateByRange`函数)并且尚未针对其他查询(即,链式代码中的`GetQueryResult`函数)应用。其他查询处于幻影风险中,因此其它查询只能用于未提交排序的只读transaction,除非应用程序可以保证,结果集在 模拟和验证/提交 之间的稳定性。 47 | 48 | 如果一个transaction通过有效性检查,committer使用write set来更新world state(世界状态)。 在更新阶段,对于write set的每个key,同一个key的world state值设置为write set 中指定的值。 此外,world state中的key的版本被改变,以反映是最新版本。 49 | 50 | 51 | ## 模拟执行和验证的例子 52 | 53 | 本节通过示例场景帮助理解语义。 在世界状态中存在一个密钥`k`,由一个元组`(k,ver,val)`表示,其中`ver`是以`val`为值的密钥`k`的最新版本。 54 | 55 | 现在,考虑一组五个transactions`T1`,`T2`,`T3`,`T4`和`T5`,全部在世界状态的相同快照上模拟。 以下显示了模拟transactions的世界状态的快照,以及每个transactions执行的读写活动的顺序。 56 | 57 | ```c 58 | World state: (k1,1,v1), (k2,1,v2), (k3,1,v3), (k4,1,v4), (k5,1,v5) 59 | T1 -> Write(k1, v1'), Write(k2, v2') 60 | T2 -> Read(k1), Write(k3, v3') 61 | T3 -> Write(k2, v2'') 62 | T4 -> Write(k2, v2'''), read(k2) 63 | T5 -> Write(k6, v6'), read(k5) 64 | ``` 65 | 现在,假定这些transactions按照T1,...,T5的顺序排序(可以包含在单个block或不同的block中) 66 | 67 | 1. `T1` 通过了有效验证,因为它不进行任何read操作。世界状态中的 `k1` 和 `k2` 更新成了 `(k1,2,v1')`, `(k2,2,v2')` 68 | 2. `T2` 没通过有效验证, 因为它读了一个key, `k1`, 而这个`k1`,在前面的transaction `T1` 中修改了。 69 | 3. `T3` 通过了有效验证,因为它不进行任何read操作。世界状态中的 `k2` 更新成了 `(k2,3,v2'')` 70 | 4. `T4` 没通过有效验证, 因为它读取了一个key, `k2`, 而这个`k1`,在前面的transaction `T1` 中修改了。 71 | 5. `T5` 通过了有效验证,因为读取的key, `k5`, `k5`没有在前面的 transactions 中修改过。 72 | 73 | 注意: 具有多个 read-write sets的 Transactions 尚不支持。 74 | 75 | 欢迎加入区块链技术交流QQ群 694125199 76 | 77 | 更多区块链知识: 78 | https://github.com/xiaofateng/knowledge-without-end/tree/master/区块链/Hyperledger 79 | 80 | 本文参考 81 | http://hyperledger-fabric.readthedocs.io/en/latest/readwrite.html 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /Hyperledger/System Chaincode .md: -------------------------------------------------------------------------------- 1 | # 介绍 2 | 用户编写的Chaincode在container中运行(本文中称为`“用户chaincode”`),并通过网络与peer进行通信。 这些Chaincode可以执行的代码有限制的。 例如,他们只能通过`“ChaincodeStub”`接口(如`GetState`或`PutState`)与peer进行交互。 Chaincode需要放宽这些限制,这样的Chaincode被广义地称为`“System Chaincode”`。 3 | 4 | System Chaincode的目的是为了定制Fabric的某些行为,如timer service 或 naming service。 5 | 6 | 设计System Chaincode最简单的方法就是和Peer一样运行它。 下文详细介绍下。 7 | 8 | ## In-process chaincode 9 | 该方法考虑作为hosting peer,运行Chaincode所需的更改和通用化。 10 | 11 | 要求 12 | 13 | * 与 用户Chaincode 共存 14 | * 坚持与 用户Chaincode 一样的生命周期 - 除了可能的“部署”(**这里根据官方文档,它与用户Chaincode的生命周期是不一样的**) 15 | * 易于安装 16 | 17 | 18 | ## Chaincode环境的重要考虑事项 19 | 对于任何Chaincode环境(容器,进程等)有两个主要考虑因素: 20 | 21 | * Runtime 注意事项 22 | * Lifecycle 注意事项 23 | 24 | ### Runtime 注意事项 25 | Runtime考虑可以大致分为Transport(传输) 和 Execution机制。 Fabric为这些机制提供的抽象,是扩展系统,以便以直接的方式支持与现有Chaincode runtimes 一致的能力。 26 | 27 | ### Transport 机制 28 | peer 和Chaincode使用`ChaincodeMessages`进行通信。 29 | 30 | * 该协议通过`ChaincodeMessage`完全封装 31 | * 目前的gRPC streaming 可以很好地映射到 Go Channels 32 | 33 | 上述内容,允许抽象处理Chaincode传输的一小部分代码,这使得大部分代码在两种环境中都是通用的。 这是runtime environments(执行环境)统一处理的关键。 34 | 35 | ### Execution机制 36 | fabric为`“vm”`提供了一个基本interface。 Docker容器是在这个抽象之上实现的。 `“vm”`(vm接口)也可用于为进程运行时提供execution environment。 这对透明化的实现所有执行访问(部署,启动,停止等)进行封装有好处。 37 | 38 | ## Lifecycle 注意事项 39 | 40 | | Life cycle | Container model | In-process Model | 41 | | --- | --- | --- | 42 | | Deploy |  通过外部request。参与共识 | 两种方法 (1) 和peer一起自动启动,不参与共识(2) 用户通过 request启动,参与共识 | 43 | 44 | # Deploying System Chaincodes 45 | 46 | System chaincodes提出了一个系统。System chaincodes将在`core.yaml`中定义。 至少,Chaincode定义将包含以下信息`{名称,路径}`。 每个Chaincode定义还可以指定 依赖的Chaincode,以便Chaincode仅在其相关Chaincode存在时才会被执行。 47 | 48 | 49 | 欢迎加入区块链技术交流QQ群 694125199 50 | 51 | 更多区块链知识: 52 | https://github.com/xiaofateng/knowledge-without-end/tree/master/区块链/Hyperledger 53 | 54 | 55 | 本文主要参考 56 | https://github.com/hyperledger/fabric/blob/release-1.1/proposals/r1/System-Chaincode-Specification.md#execution-mechanism 57 | 58 | 59 | -------------------------------------------------------------------------------- /Hyperledger/chaincode源码解读.md: -------------------------------------------------------------------------------- 1 | # 2 | 3 | shim包,为Chaincode提供了一些接口,以访问自己的state变量、transaction context,和调用其它的chaincode。 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Hyperledger/gRPC介绍.md: -------------------------------------------------------------------------------- 1 | # 介绍 2 | 3 | 在gRPC中,客户端应用程序,可以直接调用,在另一台机器上的,服务器应用程序上的方法,就好像它是本地对象一样,这使你更容易创建分布式应用程序和服务。 4 | 5 | ![](media/gPRC.png) 6 | 7 | 与许多RPC系统一样,gRPC基于定义service的想法,指定可以用参数和返回类型,远程调用的方法。 在服务器端,服务器实现此接口,并运行gRPC服务器来处理客户端调用。在客户端,客户端有一个存根(在某些语言中被称为客户端),它提供与服务器相同的方法。 8 | 9 | 默认情况,gRPC使用protocol buffers。 10 | 11 | 使用protocol buffers的第一步,是为要在proto文件中序列化的数据定义结构:这是一个带.proto扩展名的普通文本文件。 Protocol buffer数据被构造为messages,其中每条messages都是包含一系列称为字段的 name-value对的信息。这里有一个简单的例子: 12 | 13 | ```c 14 | message Person { 15 | string name = 1; 16 | int32 id = 2; 17 | bool has_ponycopter = 3; 18 | } 19 | ``` 20 | 21 | 然后,一旦指定了数据结构,就可以使用protocol buffer编译器`protoc`,从proto定义中以你的首选语言生成数据访问类。 它为每个字段提供了简单的访问器(如`name()`和`set_name()`),以及将整个结构序列化为raw bytes,和从raw bytes反序列为结构的方法。 - 例如,如果选择的语言是C++,则运行编译器上面的例子,将生成一个名为Person的类。 然后,可以在你的应用程序中使用此类来填充,序列化并检索Person protocol buffer messages。 22 | 23 | gRPC的使用,是在普通proto文件中定义gRPC services,定义PRC方法参数,和返回的类型,作为protocol buffer messages。 24 | 25 | ```c 26 | // The greeter service definition. 27 | service Greeter { 28 | // Sends a greeting 29 | rpc SayHello (HelloRequest) returns (HelloReply) {} 30 | } 31 | 32 | // The request message containing the user's name. 33 | message HelloRequest { 34 | string name = 1; 35 | } 36 | 37 | // The response message containing the greetings 38 | message HelloReply { 39 | string message = 1; 40 | } 41 | ``` 42 | gRPC 也使用了 `protoc` 作为一个特殊 gRPC 插件,来从 proto 文件生成代码。同时生成 gRPC client端和server端代码,及protocol buffer 代码。 43 | 44 | # 重要概念 45 | 46 | ## Service定义 47 | 像许多RPC系统一样,gRPC基于定义service的想法,指定可以通过参数和返回类型远程调用的方法。默认情况下,gRPC使用protocol buffers作为接口定义语言(IDL)来描述service接口和消息的结构。 如果需要,可以使用其他替代方案。 48 | 49 | ```c 50 | service HelloService { 51 | rpc SayHello (HelloRequest) returns (HelloResponse); 52 | } 53 | 54 | message HelloRequest { 55 | string greeting = 1; 56 | } 57 | 58 | message HelloResponse { 59 | string reply = 1; 60 | ``` 61 | 62 | gRPC 让你可以定义四种类型的 service 方法: 63 | 64 | * Unary RPCs, client发送一个单独的request到server端,返回一个单独的 response, 就像一个普通的函数调用。 65 | 66 | ```c 67 | rpc SayHello(HelloRequest) returns (HelloResponse){ 68 | } 69 | ``` 70 | 71 | * Server streaming RPCs,client发送一个request到server端,得到一个 stream,从stream中读取返回的一系列messages。client从返回的stream 读数据,直到没有更多的messages了。 72 | 73 | ```c 74 | rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){ 75 | } 76 | ``` 77 | 78 | * Client streaming RPCs,client写一系列的messages, 把它们发送到server, 还是使用stream。一旦 client完成了写messages, 它等待server读取这些message和返回response。 79 | 80 | ```c 81 | rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) { 82 | } 83 | ``` 84 | 85 | * Bidirectional streaming RPCs,两边都 使用read-write stream,发送一系列的 messages。这两个 streams 单独操作, 因此 clients 和 servers 可以以它们喜欢的顺序读和写: 例如, server 可以 在写responses之前,等待接收所有的client messages, 或者,可以读一条message ,然后 写一条 message, 或者其它的读写结合方式。 每个stream里面的message的顺序是保留的。 86 | 87 | ```c 88 | rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){ 89 | } 90 | ``` 91 | 92 | ## 使用API 93 | gRPC用户,通常在client端调用这些API,并在server端实现相应的API。 94 | 95 | 在server端,服务器实现由service声明的方法,并运行gRPC server来处理客户端的调用。 gRPC基础设施解码传入的请求,执行service methods并编码service responses。 96 | 97 | 在客户端,客户端拥有一个称为存根的局部对象(对于某些语言,一般称为客户端),它实现与service相同的方法。 然后,客户端可以在本地对象上调用这些方法,并将该调用的参数,包装在适当的protocol buffer message类型中 - gRPC将这个请求发送到服务器,并在服务器返回的protocol buffer response之后进行查找。 98 | 99 | ## 同步和异步 100 | 101 | 同步RPC调用会阻塞,直到服务器的response,与RPC期望的过程调用的抽象最接近。另一方面,网络本质上是异步的,在许多情况下,能够启动RPC而不阻塞当前线程是很有用。 102 | 103 | 大多数语言的gRPC编程界面都具有同步和异步风格。可以在每种语言的教程和参考文档中找到更多内容 104 | 105 | # RPC life cycle 106 | 107 | ## Unary RPC(一元RPC) 108 | 109 | 先看最简单的RPC类型,客户端发送单个请求并获取单个响应。 110 | 111 | * 客户端调用存根/客户端对象上的方法后,server会受到通知,RPC被调用了,同时传递,client端的metadata,method name和指定的截止日期。 112 | 113 | * 然后,服务器可以立即发送自己的初始metadata(必须在任何响应之前发送),或等待client的请求消息(首先发生的是特定于应用程序的请求消息)。 114 | 115 | * 一旦服务器获得了客户端的请求消息,它就会执行任何必要的工作来创建和产生response。然后将response(如果成功)与状态详细信息(状态码和可选状态消息),以及可选的尾随metadata,一起返回给客户端。 116 | * 如果状态正常,则客户端将获得响应,从而完成客户端的呼叫。 117 | 118 | 其它的与此类似,不再详述。 119 | 120 | ## Deadlines/Timeouts 121 | 122 | gRPC允许客户指定他们愿意等待RPC完成多久,然后RPC以`DEADLINE_EXCEEDED`错误终止。 在服务器端,服务器可以查询特定的RPC是否超时,或者剩下多少时间来完成RPC。 123 | 124 | 如何指定截止日期或超时时间因语言而异,例如,并非所有语言都有默认截止日期,某些语言API根据截止日期(固定时间点)工作,某些语言API在超时方面起作用 (持续时间)。 125 | 126 | 127 | ## RPC终止 128 | 在gRPC中,客户端和服务器,都对call的成功进行了独立的和本地的确定,其结论可能不匹配。例如,你可以在服务器端成功完成RPC(“我已发送所有响应!”),但在客户端失败(“响应在我的截止日期后到达!”)。 服务器也可以决定,在客户端发送所有请求之前完成,导致PRC终止。 129 | 130 | 131 | ## 取消RPC 132 | 客户端或服务器可以随时取消RPC。 取消操作会立即终止RPC,不再进行任何事情。 133 | 注意,它不是“撤消”:取消之前所做的更改不会被回滚。 134 | 135 | 136 | ## 元数据 137 | 元数据是关于特定RPC调用的信息(例如认证细节),以键值对列表的形式出现,其中键是字符串,值通常是字符串(但可以是二进制数据)。 元数据对gRPC本身是不透明的 - 它允许客户端提供与服务器调用相关的信息,反之亦然。 138 | 对元数据的访问取决于语言。 139 | 140 | 141 | ## Channels 142 | gRPC Channel提供到指定主机和端口上的,gRPC服务的连接,并在创建客户端存根(或某些语言中的“客户端”)时使用。 客户端可以指定Channel参数来修改gRPC的默认行为,例如打开和关闭消息压缩。 Channel具有状态,包括已连接和空闲状态。 143 | gRPC如何处理关闭Channel取决于语言。 有些语言也允许查询Channel状态。 144 | 145 | 146 | -------------------------------------------------------------------------------- /Hyperledger/media/ .png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaofateng/knowledge-without-end/79f8fa6ad3393cf8ece5ad5e1eac3d74344c2e22/Hyperledger/media/ .png -------------------------------------------------------------------------------- /Hyperledger/media/15210198930172/15210201011823.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaofateng/knowledge-without-end/79f8fa6ad3393cf8ece5ad5e1eac3d74344c2e22/Hyperledger/media/15210198930172/15210201011823.jpg -------------------------------------------------------------------------------- /Hyperledger/media/MSPElemment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaofateng/knowledge-without-end/79f8fa6ad3393cf8ece5ad5e1eac3d74344c2e22/Hyperledger/media/MSPElemment.png -------------------------------------------------------------------------------- /Hyperledger/media/MSPLevel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaofateng/knowledge-without-end/79f8fa6ad3393cf8ece5ad5e1eac3d74344c2e22/Hyperledger/media/MSPLevel.png -------------------------------------------------------------------------------- /Hyperledger/media/MSPOrgRelation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaofateng/knowledge-without-end/79f8fa6ad3393cf8ece5ad5e1eac3d74344c2e22/Hyperledger/media/MSPOrgRelation.png -------------------------------------------------------------------------------- /Hyperledger/media/MSPxmind.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaofateng/knowledge-without-end/79f8fa6ad3393cf8ece5ad5e1eac3d74344c2e22/Hyperledger/media/MSPxmind.png -------------------------------------------------------------------------------- /Hyperledger/media/MSPxmind2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaofateng/knowledge-without-end/79f8fa6ad3393cf8ece5ad5e1eac3d74344c2e22/Hyperledger/media/MSPxmind2.png -------------------------------------------------------------------------------- /Hyperledger/media/channelArtifacts.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaofateng/knowledge-without-end/79f8fa6ad3393cf8ece5ad5e1eac3d74344c2e22/Hyperledger/media/channelArtifacts.jpg -------------------------------------------------------------------------------- /Hyperledger/media/cryconfig01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaofateng/knowledge-without-end/79f8fa6ad3393cf8ece5ad5e1eac3d74344c2e22/Hyperledger/media/cryconfig01.png -------------------------------------------------------------------------------- /Hyperledger/media/crypto-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaofateng/knowledge-without-end/79f8fa6ad3393cf8ece5ad5e1eac3d74344c2e22/Hyperledger/media/crypto-config.png -------------------------------------------------------------------------------- /Hyperledger/media/dockerContainers.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaofateng/knowledge-without-end/79f8fa6ad3393cf8ece5ad5e1eac3d74344c2e22/Hyperledger/media/dockerContainers.jpg -------------------------------------------------------------------------------- /Hyperledger/media/gPRC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaofateng/knowledge-without-end/79f8fa6ad3393cf8ece5ad5e1eac3d74344c2e22/Hyperledger/media/gPRC.png -------------------------------------------------------------------------------- /Hyperledger/media/localAndchannelMSPS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaofateng/knowledge-without-end/79f8fa6ad3393cf8ece5ad5e1eac3d74344c2e22/Hyperledger/media/localAndchannelMSPS.png -------------------------------------------------------------------------------- /Hyperledger/media/transactionFlow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaofateng/knowledge-without-end/79f8fa6ad3393cf8ece5ad5e1eac3d74344c2e22/Hyperledger/media/transactionFlow.png -------------------------------------------------------------------------------- /Hyperledger/使用go编写gPRC服务.md: -------------------------------------------------------------------------------- 1 | # 准备工作 2 | 3 | ## 安装go 4 | 需要版本1.6以上 5 | 6 | ## 安装gPRC 7 | 8 | ```c 9 | go get -u google.golang.org/grpc 10 | ``` 11 | 12 | ## 安装 Protocol Buffers v3 13 | 14 | * 安装 protoc 编译器,用来生成 gRPC service 代码。最简单的方式是下载你的平台对应的 pre-compiled binaries (protoc--.zip),下载地址 https://github.com/google/protobuf/releases 15 | * 解压文件。 16 | * 更新环境变量PATH,加入 protoc binary file 17 | * 然后,为Go安装 protoc 插件。 18 | 19 | ```c 20 | go get -u github.com/golang/protobuf/protoc-gen-go 21 | ``` 22 | compiler 插件, protoc-gen-go, 会安装在 $GOPATH/bin目录 23 | 24 | # Example演示 25 | 26 | example例子位置 27 | 28 | ```c 29 | $GOPATH/src/google.golang.org/grpc/examples 30 | ``` 31 | 32 | 到example目录下 33 | 34 | ```c 35 | cd $GOPATH/src/google.golang.org/grpc/examples/helloworld 36 | ``` 37 | 38 | protoc编译器,根据.proto文件,生成相应的 .pb.go文件 39 | 40 | 本例中,helloworld.pb.go 文件已经存在,它包含: 41 | 42 | * 生成的 client 和 server 代码。 43 | * 填充, 序列化, 检索 HelloRequest 和 HelloReply message types的代码。 44 | 45 | ## 运行 46 | 编译和运行server 和 client 代码, 使用go run命令。在examples helloworld目录下: 47 | 48 | ```c 49 | go run greeter_server/main.go 50 | ``` 51 | 另外一个终端: 52 | 53 | ```c 54 | go run greeter_client/main.go 55 | ``` 56 | 在client端会看到 Hello world。说明运行gRPC服务成功了。 57 | 58 | # Go开发 59 | 主要介绍内容如下: 60 | * 在 .proto 文件中,定义service。 61 | * 使用protocol buffer compiler 生成server 和 client代码。 62 | * 使用Go gRPC API 写一个简单的client 和 server。 63 | 64 | ## 定义 service 65 | 66 | 首先,使用protocol buffers,定义gRPC service 和 method request、 response types。 文件目录`examples/route_guide/routeguide/route_guide.proto` 67 | 68 | 在 .proto 文件中定义service: 69 | 70 | ```c 71 | service RouteGuide { 72 | ... 73 | } 74 | ``` 75 | 76 | ## 定义rpc methods 77 | 在 service 定义中 定义rpc methods, 指定request 和 response types。gRPC 允许定义四种类型的 service method, 都在RouteGuide service中: 78 | 79 | A simple RPC 80 | 81 | ```c 82 | // Obtains the feature at a given position. 83 | rpc GetFeature(Point) returns (Feature) {} 84 | ``` 85 | 86 | A server-side streaming RPC 87 | 88 | ```c 89 | //获得给定矩形内的可用特征。 结果是流式传输而不是立即返回 90 | //(例如,在一个响应消息中使用重复字段), 91 | //因为矩形可能会覆盖大领域并包含大量的功能。 92 | rpc ListFeatures(Rectangle) returns (stream Feature) {} 93 | ``` 94 | 95 | A client-side streaming RPC 96 | 97 | ```c 98 | // Accepts a stream of Points on a route being traversed, returning a 99 | // RouteSummary when traversal is completed. 100 | rpc RecordRoute(stream Point) returns (RouteSummary) {} 101 | ``` 102 | 103 | A bidirectional streaming RPC 104 | 105 | ```c 106 | // Accepts a stream of RouteNotes sent while a route is being traversed, 107 | // while receiving other RouteNotes (e.g. from other users). 108 | rpc RouteChat(stream RouteNote) returns (stream RouteNote) {} 109 | ``` 110 | 111 | .proto 同时包含了 protocol buffer message type 定义 Point message type: 112 | 113 | ```c 114 | // Points 代表经度-纬度 ,以 E7 形式。 115 | // (degrees multiplied by 10**7 and rounded to the nearest integer). 116 | // Latitudes should be in the range +/- 90 degrees and longitude should be in 117 | // the range +/- 180 degrees (inclusive). 118 | message Point { 119 | int32 latitude = 1; 120 | int32 longitude = 2; 121 | } 122 | ``` 123 | 124 | ## 生成 client 和 server 代码 125 | 126 | 下一步是从.proto service 定义,生成 gRPC client 和 server 接口 。 127 | route_guide example目录下运行 : 128 | 129 | ```c 130 | protoc -I routeguide/ routeguide/route_guide.proto --go_out=plugins=grpc:routeguide 131 | ``` 132 | 执行完,在routeguide目录中生成route_guide.pb.go文件。 133 | 这个文件中包含下面信息: 134 | 135 | * 所有的填充、序列化、检索request and response message types的protocol buffer代码。 136 | * clients调用 RouteGuide service中定义的方法的interface type (或者 stub存根)。 137 | * servers 要实现的interface type, 定义在 RouteGuide service中的方法。 138 | 139 | ## 创建server 140 | 141 | 有两部分: 142 | * 实现从service定义生成的service interface : 做真正的工作。 143 | * 运行 gRPC server 监听 client端的requests, 并分发到正确的service实现. 144 | 145 | ### 实现 RouteGuide 146 | server有一个routeGuideServer struct type, 它实现生成的RouteGuideServer接口: 147 | 148 | ```c 149 | type routeGuideServer struct { 150 | ... 151 | } 152 | ... 153 | 154 | func (s *routeGuideServer) GetFeature(ctx context.Context, point *pb.Point) (*pb.Feature, error) { 155 | ... 156 | } 157 | ... 158 | 159 | func (s *routeGuideServer) ListFeatures(rect *pb.Rectangle, stream pb.RouteGuide_ListFeaturesServer) error { 160 | ... 161 | } 162 | ... 163 | 164 | func (s *routeGuideServer) RecordRoute(stream pb.RouteGuide_RecordRouteServer) error { 165 | ... 166 | } 167 | ... 168 | 169 | func (s *routeGuideServer) RouteChat(stream pb.RouteGuide_RouteChatServer) error { 170 | ... 171 | } 172 | ... 173 | ``` 174 | 175 | #### Simple RPC 176 | routeGuideServer 实现所有的service methods。先看最简单的, GetFeature, 只从client获取一个 Point ,从它的Feature数据库中,返回相应的feature信息。 177 | 178 | ```c 179 | func (s *routeGuideServer) GetFeature(ctx context.Context, point *pb.Point) (*pb.Feature, error) { 180 | for _, feature := range s.savedFeatures { 181 | if proto.Equal(feature.Location, point) { 182 | return feature, nil 183 | } 184 | } 185 | // No feature was found, return an unnamed feature 186 | return &pb.Feature{"", point}, nil 187 | } 188 | ``` 189 | 190 | ## 启动 server 191 | 192 | 指定我们想监听client requests 的端口 193 | 使用grpc.NewServer()创建一个gRPC server实例。 194 | 把我们的service 实现 注册到 gRPC server. 195 | 调用 Serve(), 会阻塞直到killed 或 Stop()。 196 | 197 | ```c 198 | flag.Parse() 199 | lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port)) 200 | if err != nil { 201 | log.Fatalf("failed to listen: %v", err) 202 | } 203 | grpcServer := grpc.NewServer() 204 | pb.RegisterRouteGuideServer(grpcServer, &routeGuideServer{}) 205 | ... // determine whether to use TLS 206 | grpcServer.Serve(lis) 207 | ``` 208 | ## 创建client 209 | 210 | 现在,为我们的RouteGuide service创建客户端. 211 | 完整代码是 `grpc-go/examples/route_guide/client/client.go`. 212 | 213 | ### 创建 stub 214 | **为了调用后service methods,我们首先创建一个 gRPC channel 来和 server通信。** 215 | 我们通过传递server地址 和 端口号到grpc.Dial(),来创建。 216 | 217 | ```c 218 | conn, err := grpc.Dial(*serverAddr) 219 | if err != nil { 220 | ... 221 | } 222 | defer conn.Close() 223 | ``` 224 | 225 | 可以使用DialOptions来设置 auth credentials (例如 TLS, GCE credentials, JWT credentials等)。 226 | 227 | 一旦gRPC channel创建了, 我们需要一个 client stub 来执行 RPCs. 我们使用pb包中提供的 NewRouteGuideClient 方法(从.proto文件中生成的) 228 | 229 | `client := pb.NewRouteGuideClient(conn)` 230 | 231 | ### 调用service methods 232 | 233 | 在 gRPC-Go中, RPCs是 blocking/synchronous 模式,即, RPC 调用阻塞等待server响应。 234 | 235 | Simple RPC 236 | 调用simple RPC GetFeature 就像调用一个本地方法一样。 237 | 238 | ```c 239 | feature, err := client.GetFeature(context.Background(), &pb.Point{409146138, -746188906}) 240 | if err != nil { 241 | ... 242 | } 243 | ``` 244 | 如果不报错,可以读取response的消息 245 | `log.Println(feature)` 246 | 247 | ##运行 248 | 在 249 | ```c 250 | $GOPATH/src/google.golang.org/grpc/examples/route_guide 251 | ``` 252 | 目录下执行 253 | `go run server/server.go` 254 | 在另一个终端执行 255 | `go run client/client.go` 256 | 257 | -------------------------------------------------------------------------------- /Hyperledger/向一个Channel中添加Org.md: -------------------------------------------------------------------------------- 1 | # 环境准备 2 | 本文,根据byfn例子,介绍手动向Channel中添加一个Org的详细步骤。 3 | ## 日志配置 4 | 5 | 将`cli` 和 `Org3cli` containers的日志级别`CORE_LOGGING_LEVEL`修改为 `DEBUG` 6 | 7 | 修改`first-network`目录中的`docker-compose-cli.yaml`文件, 8 | 9 | ```c 10 | cli: 11 | container_name: cli 12 | image: hyperledger/fabric-tools:$IMAGE_TAG 13 | tty: true 14 | stdin_open: true 15 | environment: 16 | - GOPATH=/opt/gopath 17 | - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock 18 | #- CORE_LOGGING_LEVEL=INFO 19 | - CORE_LOGGING_LEVEL=DEBUG 20 | ``` 21 | 22 | 修改相同目录下的`docker-compose-org3.yaml`文件 23 | 24 | ```c 25 | Org3cli: 26 | container_name: Org3cli 27 | image: hyperledger/fabric-tools:$IMAGE_TAG 28 | tty: true 29 | stdin_open: true 30 | environment: 31 | - GOPATH=/opt/gopath 32 | - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock 33 | #- CORE_LOGGING_LEVEL=INFO 34 | - CORE_LOGGING_LEVEL=DEBUG 35 | ``` 36 | 37 | ## 清理环境 38 | 如果启动过`eyfn` 39 | `./eyfn.sh down` 40 | 41 | 停掉container等 42 | `./byfn.sh -m down` 43 | 44 | 生成默认的 BYFN artifacts: 45 | `./byfn.sh -m generate` 46 | 47 | 启动网络 48 | `./byfn.sh -m up` 49 | 50 | # 生成 Org3 Crypto Material 51 | 52 | 从`first-network`切换到`org3-artifacts`子目录。 53 | 54 | `cd org3-artifacts` 55 | 56 | 这里有两个有趣的yaml文件:`org3-crypto.yaml`和`configtx.yaml`。首先,为`Org3`生成加密资料: 57 | 58 | ```c 59 | ../../../bin/cryptogen generate --config=./org3-crypto.yaml 60 | ``` 61 | 62 | 该命令读入我们新的加密yaml文件 -`·org3-crypto.yaml` - 并利用`cryptogen`为`Org3` CA,以及与此新Org绑定的两个peer,生成密钥和证书。与BYFN实现一样,这个加密资料被放入当前工作目录(在我们的例子中为`org3-artifacts`)内新生成的`crypto-config`文件夹中。 63 | 64 | 现在使用`configtxgen`工具在JSON中打印出特定于`Org3`的配置材料。我们告诉工具在当前目录中查找它需要引用的`configtx.yaml`。 65 | 66 | ```c 67 | export FABRIC_CFG_PATH=$PWD && ../../../bin/configtxgen -printOrg Org3MSP > ../channel-artifacts/org3.json 68 | ``` 69 | 上述命令创建一个JSON文件 - `org3.json`,并将其输出到`first-network`目录下的`channel-artifacts`子目录中。该文件包含Org3的策略定义,以及以base 64格式提供的三个重要证书:admin用户证书(稍后将用作Org3的管理员),CA根证书和TLS根证书。**在后面的步骤中,我们将把这个JSON文件附加到Channel配置中。** 70 | 71 | **最后一项工作是将`Orderer Org`的`MSP资料`移到Org3 `crypto-config`目录中。**特别是,我们关注Orderer的TLS根证书,这将允许Org3实体和网络的ordering节点之间的安全通信。 72 | 73 | ```c 74 | cd ../ && cp -r crypto-config/ordererOrganizations org3-artifacts/crypto-config/ 75 | ``` 76 | 77 | 现在我们准备更新Channel配置... 78 | 79 | # 准备CLI环境 80 | 81 | 更新过程中,需要使用配置转换器工具 - `configtxlator`。该工具,提供独立于SDK的无状态`REST API`。此外,它还提供CLI,以简化Fabric网络中的配置任务。**该工具允许,在不同的等效数据表示/格式(本例中的情况,是protobufs和JSON之间)之间轻松转换。另外,该工具可以根据两个channel配置之间的差异来计算配置更新transaction。** 82 | 83 | 首先,exec进入CLI容器。回想一下,这个容器已经安装了`BYFN`加密配置库,让我们可以访问两个原始peer组织和`Orderer Org`的MSP资料。bootstrapped identity 是Org1管理员用户,这意味着我们想要充当Org2的任何步骤,都需要导出MSP特定的环境变量。 84 | 命令如下: 85 | `docker exec -it cli bash` 86 | 87 | 现在将jq工具安装到容器中。该工具允许,脚本与由`configtxlator`工具返回的JSON文件进行交互: 88 | 89 | `apt update && apt install -y jq` 90 | 91 | 导出`ORDERER_CA`和`CHANNEL_NAME`变量: 92 | 93 | ```c 94 | export ORDERER_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem && export CHANNEL_NAME=mychannel 95 | ``` 96 | 97 | 检查以确保变量已正确设置: 98 | 99 | `echo $ORDERER_CA && echo $CHANNEL_NAME` 100 | 101 | # Fetch the Configuration 102 | 103 | 现在,我们有一个带有两个关键环境变量的CLI容器 - `ORDERER_CA`和`CHANNEL_NAME`。让我们去获取channel的最新配置块 - mychannel。 104 | 105 | 我们不得不,拉取配置的最新版本的原因,是因为channel配置元素是版本化的。版本管理很重要,原因有几个: 106 | 107 | * 它可以防止重复或重放配置更改(例如,使用旧CRL恢复到channel配置会带来安全风险)。 108 | * 此外,它有助于确保并发性(如果您希望从您的channel中移除组织,例如,在添加新组织后,版本控制将防止你误删除两个组织。仅仅会删除,你想要删除的组织。) 109 | 110 | ```c 111 | peer channel fetch config config_block.pb -o orderer.example.com:7050 -c $CHANNEL_NAME --tls --cafile $ORDERER_CA 112 | ``` 113 | 该命令将二进制protobuf channel配置块保存到`config_block.pb`(pb是protobuf的简写)。请注意,名称和文件扩展名的选择是任意的。但是,建议遵循一个约定,来标识所表示的对象的类型及其编码(protobuf或JSON 编码格式)。 114 | 115 | 当您发布`peer channel fetch`命令时,终端中的输出量很大。日志中的最后一行很有用: 116 | 117 | ```c 118 | 2017-11-07 17:17:57.383 UTC [channelCmd] readBlock -> DEBU 011 Received block: 2 119 | ``` 120 | 121 | 这告诉我们,mychannel的最新配置块实际上是块2,而不是起始块。默认情况下,`peer channel fetch`命令返回目标channel的最新配置块,在本例中为第三个块(块从0开始计数)。这是因为BYFN脚本在两个单独的Channel更新transaction中,为我们的两个组织(Org1和Org2)定义了Anchor peers。 122 | 123 | 因此,我们有以下配置顺序: 124 | 125 | ```c 126 | block 0: genesis block 127 | block 1: Org1 anchor peer update 128 | block 2: Org2 anchor peer update 129 | ``` 130 | 131 | 132 | # 将配置转换为JSON并Trim It Down(修剪) 133 | 134 | 现在我们将使用`configtxlator`工具,将此Channel配置块 解码为JSON格式(这种json格式,可由人读取和修改)。我们还必须删除,所有与我们想要改变无关的header,metadata, creator signatures等等。我们通过jq工具来实现这一点: 135 | 136 | ```c 137 | configtxlator proto_decode --input config_block.pb --type common.Block | jq .data.data[0].payload.data.config > config.json 138 | ``` 139 | 140 | 这给我们留下了一个修剪后的JSON对象--`config.json`,位于`first-network`中`fabric-samples`文件夹中(在cli container中路径是 `/opt/gopath/src/github.com/hyperledger/fabric/peer`,至于`fabric-samples`中没找到),它将作为我们配置更新的baseline。 141 | 142 | 阅读此文件,值得研究,因为它揭示了底层的配置结构,和可以进行的其他类型的Channel更新。 143 | 144 | # Add the Org3 Crypto Material 145 | 146 | 无论您尝试进行哪种配置更新,您采取的步骤都将几乎相同。 147 | 我们选择在本教程中添加一个组织,因为它是您可以尝试的最复杂的channel配置更新。 148 | 149 | 我们将再次使用`jq`工具,将`Org3`配置定义(`org3.json`)附加到channel的应用程序groups字段,并将输出命名为`modified_config.json`。 150 | 151 | ```c 152 | jq -s '.[0] * {"channel_group":{"groups":{"Application":{"groups": {"Org3MSP":.[1]}}}}}' config.json ./channel-artifacts/org3.json > modified_config.json 153 | ``` 154 | 现在,在CLI容器中,我们有两个感兴趣的JSON文件--`config.json`和`modified_config.json`。初始文件仅包含`Org1`和`Org2`资料,而“修改”文件包含全部三个Orgs。此时,只需重新编码这两个JSON文件并计算增量即可。 155 | 156 | 首先,将`config.json`翻译成名为`config.pb`的protobuf: 157 | 158 | ```c 159 | configtxlator proto_encode --input config.json --type common.Config --output config.pb 160 | ``` 161 | 162 | 接下来,将`modified_config.json`编码为`modified_config.pb`: 163 | 164 | ```c 165 | configtxlator proto_encode --input modified_config.json --type common.Config --output modified_config.pb 166 | ``` 167 | 168 | 现在使用`configtxlator`来计算这两个配置`protobufs`之间的增量。该命令将输出一个名为`org3_update.pb`的新protobuf二进制文件: 169 | 170 | ```c 171 | configtxlator compute_update --channel_id $ CHANNEL_NAME --original config.pb --updated modified_config.pb --output org3_update.pb 172 | ``` 173 | 174 | 这个新的原型 - `org3_update.pb` - 包含Org3定义和指向Org1和Org2资料的高级指针。我们可以放弃针对Org1和Org2的广泛的MSP资料和修改政策信息,因为这些数据已经存在于Channel的genesis block中。因此,我们只需要两种配置之间的增量(delta)。 175 | 176 | 在提交Channel更新之前,我们需要执行一些最后步骤。首先,让我们将此对象,解码为可编辑的JSON格式,并将其称为`org3_update.json`: 177 | 178 | ```c 179 | configtxlator proto_decode --input org3_update.pb --type common.ConfigUpdate | jq . > org3_update.json 180 | ``` 181 | 182 | 现在,我们有一个解码的更新文件 - `org3_update.json` - 我们需要将其封装在信封消息中。这一步将,给回我们之前剥去的header 字段。我们将这个文件命名为`org3_update_in_envelope.json`: 183 | 184 | ```c 185 | echo '{"payload":{"header":{"channel_header":{"channel_id":"mychannel", "type":2}},"data":{"config_update":'$(cat org3_update.json)'}}}' | jq . > org3_update_in_envelope.json 186 | ``` 187 | 188 | 使用我们正确构建的JSON - `org3_update_in_envelope.json `- 我们将最后一次利用`configtxlator`工具并将其转换为Fabric所需的完全成熟的protobuf格式。我们将命名我们的最终更新对象`org3_update_in_envelope.pb`: 189 | 190 | ```c 191 | configtxlator proto_encode --input org3_update_in_envelope.json --type common.Envelope --output org3_update_in_envelope.pb 192 | ``` 193 | 194 | 195 | # Sign and Submit the Config Update(签署并提交配置更新) 196 | 197 | 现在在我们的CLI容器中,有一个protobuf二进制文件 - `org3_update_in_envelope.pb`。但是,在将配置写入ledger之前,我们需要必要的Admin用户签名。 198 | 199 | 我们channel Application group的修改政策(mod_policy)被设置为默认`“MAJORITY”`,这意味着我们需要大多数现有组织管理员对其进行签名。因为我们只有两个组织--`Org1`和`Org2` - 而其中大部分是两个,我们需要他们两个签名。如果没有两个签名,ordering service将拒绝,未完成该政策的交易。 200 | 201 | 首先,让我们以`Org1` Admin的身份签署这个更新协议。请记住,CLI容器是使用Org1 MSP资料引导的,所以我们只需发出peer channel `signconfigtx`命令: 202 | 203 | ```c 204 | peer channel signconfigtx -f org3_update_in_envelope.pb 205 | ``` 206 | 207 | 最后一步是切换CLI容器的身份以反映`Org2` Admin用户。我们通过导出特定于`Org2` MSP的四个环境变量来实现这一点。 208 | 209 | 注意: 210 | 在组织之间切换,以签署配置事务(或执行其他任何操作)并不是真实生产环境的Fabric操作。 一个容器永远不会与整个网络的加密资料一起安装。(本文中,只是测试环境。)相反,配置更新,需要安全地通过带外传递给Org2管理员进行检查和批准。 211 | 212 | 导出 `Org2` 环境变量: 213 | 214 | ```c 215 | export CORE_PEER_LOCALMSPID="Org2MSP" 216 | 217 | export CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt 218 | 219 | export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp 220 | 221 | export CORE_PEER_ADDRESS=peer0.org2.example.com:7051 222 | ``` 223 | 224 | 225 | 最后,我们将发出peer channel更新命令。 Org2管理员签名将附加到此调用中,因此不需要再次手动签署protobuf: 226 | 227 | (注意:即将到来的ordering service更新调用,将接受一系列系统签名和政策检查。因此,您可能发现,流式传输并检查ordering节点的日志很有用。从另一个shell中,发出`docker logs -f orderer.example.com`命令以显示它们。) 228 | 229 | 发送更新调用: 230 | 231 | ```c 232 | peer channel update -f org3_update_in_envelope.pb -c $CHANNEL_NAME -o orderer.example.com:7050 --tls --cafile $ORDERER_CA 233 | 234 | ``` 235 | 如果您的更新已成功提交,您应该看到与以下内容类似的消息摘要: 236 | 237 | ```c 238 | 2018-02-24 18:56:33.499 UTC [msp / identity] Sign - > DEBU 00f Sign:digest:3207B24E40DE2FAB87A2E42BC004FEAA1E6FDCA42977CB78C64F05A88E556ABA 239 | ``` 240 | 241 | 您还将看到我们的配置事务提交: 242 | 243 | ```c 244 | 2018-02-24 18:56:33.499 UTC [channelCmd] update -> INFO 010 Successfully submitted channel update 245 | ``` 246 | 247 | 成功的channel更新调用,向Channel上的所有peer返回一个新的块 - 块5。如果你还记得,块0-2是初始Channel配置,而块3和块4是mycc chaincode的实例化和调用。同样,块5作为Channel上现在定义的Org3的最新Channel配置。 248 | 249 | 检查`peer0.org1.example.com`的日志: 250 | 251 | `docker logs -f peer0.org1.example.com` 252 | 253 | # Configuring Leader Election 254 | 255 | 注意:本部分作为通用参考,用于了解在初始channel配置完成后,向组织添加网络时的leader选举设置。 此示例默认为动态leader选举,这是为,peer-base.yaml中网络中的所有peer设置的。 256 | 257 | 新加入的peer由genesis块引导,其中不包含有关在channel配置更新中,添加的组织的信息。因此,新peer无法利用gossip,因为他们无法验证,自己组织中由其它peer转发的块,直到他们获得,将该组织添加到Channel的配置事务。因此,新添加的peer必须具有以下配置之一,以便他们接收来自ordering service的数据块: 258 | 259 | 1.要使用静态leader模式,请将peer配置为组织leader: 260 | 261 | ```c 262 | CORE_PEER_GOSSIP_USELEADERELECTION=false 263 | CORE_PEER_GOSSIP_ORGLEADER=true 264 | ``` 265 | 266 | 注意:对于添加到channel的所有新peer,此配置必须相同。 267 | 268 | 2.为了利用动态leader选举,配置peer使用leader选举: 269 | 270 | ```c 271 | CORE_PEER_GOSSIP_USELEADERELECTION=true 272 | CORE_PEER_GOSSIP_ORGLEADER=false 273 | ``` 274 | 275 | 注意:由于新添加的组织的peer,将无法形成成员资格视图,因此每个peer将开始宣称自己是leader,此选项与静态配置类似。但是,一旦他们获得了,将组织添加到Channel的配置事务的更新,组织将只有一个活动leader。因此,如果您最终希望组织的peer利用leader选举,建议您利用此选项。 276 | 277 | # Join Org3 to the Channel 278 | todo 279 | # Upgrade and Invoke Chaincode 280 | todo 281 | 282 | 283 | 284 | 欢迎加入区块链技术交流QQ群 694125199 285 | 286 | 更多区块链知识: 287 | https://github.com/xiaofateng/knowledge-without-end/tree/master/区块链/Hyperledger 288 | 289 | http://hyperledger-fabric.readthedocs.io/en/latest/channel_update_tutorial.html 290 | 291 | 292 | -------------------------------------------------------------------------------- /Hyperledger/开发Chaincode .md: -------------------------------------------------------------------------------- 1 | ## Chaincode简介 2 | 3 | chaincode通常处理由网络成员赞同的业务逻辑,因此它类似于“智能合约”。 可以调用chaincode来更新或查询提案交易中的ledger。 4 | 5 | 如果有适当的许可,chaincode可以调用另一个chaincode,以访问其状态,无论是在同一个Channel还是在不同的Channel中。 请注意,如果被调用的chaincode与调用chaincode位于不同的通道上,则只允许读取查询。 6 | 7 | 8 | ## Chaincode API 9 | 每个chaincode程序必须实现`Chaincode interface`,其方法被调用以回应收到的交易。 10 | 11 | 特别是当Chaincode接收实例化或升级transaction时,将调用Init方法,以便Chaincode可以执行任何必要的初始化,包括应用程序状态的初始化。 调用Invoke方法是为了响应接收调用transaction来处理transaction提议。 12 | 13 | chaincode “shim”API中的另一个接口是`ChaincodeStubInterface`,用于访问和修改ledger,并在chaincode之间进行调用。 14 | 15 | 在本文中,我们将通过实现一个管理“资产”的简单chaincode应用程序来演示如何使用这些API。 16 | 17 | ## 简单的资产Chaincode 18 | 我们的应用程序是一个基本样本chaincode,用于在ledger上创建资产(key-value对)。 19 | 20 | ### 选择代码的位置 21 | 22 | 现在,需要为chaincode应用程序创建一个目录作为`$GOPATH/src/`的子目录。 23 | 24 | 为了简单起见,我们使用下面的命令: 25 | 26 | ```c 27 | mkdir -p $GOPATH/src/sacc && cd $GOPATH/src/sacc 28 | ``` 29 | 30 | 现在,创建将用代码填充的源文件: 31 | 32 | `touch sacc.go` 33 | 34 | 35 | ### Housekeeping 36 | 37 | 首先,我们从一些Housekeeping开始。**与每个chaincode一样,它实现了Chaincode接口,即`Init`和`Invoke`函数。** 因此,让我们添加go import语句以获取chaincode的必要依赖关系。 我们将导入chaincode shim包和peer protobuf包。 接下来,让我们添加一个结构`SimpleAsset`作为Chaincode shim函数的接收器。 38 | 39 | ```c 40 | package main 41 | 42 | import ( 43 | "fmt" 44 | 45 | "github.com/hyperledger/fabric/core/chaincode/shim" 46 | "github.com/hyperledger/fabric/protos/peer" 47 | ) 48 | 49 | // SimpleAsset 实现管理asset的一个简单chaincode 50 | type SimpleAsset struct { 51 | } 52 | ``` 53 | 54 | ### Initializing the Chaincode 55 | 56 | 下面,我们实现 `Init` 函数. 57 | 58 | ```c 59 | // chaincode 实例化的时候,调用Init来初始化数据 60 | func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response { 61 | 62 | } 63 | ``` 64 | 65 | 请注意,chaincode升级也会调用此函数。在编写将升级现有chaincode的代码时,请确保适当地修改`Init`函数。 特别是,如果没有“migration”(迁移),或没有任何东西需要作为升级的一部分进行初始化时,请提供一个空的`“Init”`方法。 66 | 67 | 接下来,我们将使用`ChaincodeStubInterface.GetStringArgs`函数检索`Init`调用的参数,并检查其有效性。 在我们的例子中,我们期待一个key-value对。 68 | 69 | ```c 70 | func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response { 71 | // 从 transaction proposal 获取参数 72 | args := stub.GetStringArgs() 73 | if len(args) != 2 { 74 | return shim.Error("Incorrect arguments. Expecting a key and a value") 75 | } 76 | } 77 | ``` 78 | 79 | 接下来,已经确定该调用是有效的,我们将把初始状态存储在ledger中。 要做到这一点,我们将调用`ChaincodeStubInterface.PutState`,并将key和value作为参数传入。 假设一切顺利,会返回一个指示初始化成功的`peer.Response`对象。 80 | 81 | ```c 82 | func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response { 83 | // 从 transaction proposal 获取参数 84 | args := stub.GetStringArgs() 85 | if len(args) != 2 { 86 | return shim.Error("Incorrect arguments. Expecting a key and a value") 87 | } 88 | 89 | // 通过调用 stub.PutState(),设置任何 variables或assets 90 | // 我们把 key 和 value 存储在ledger上 91 | err := stub.PutState(args[0], []byte(args[1])) 92 | if err != nil { 93 | return shim.Error(fmt.Sprintf("Failed to create asset: %s", args[0])) 94 | } 95 | return shim.Success(nil) 96 | } 97 | ``` 98 | 99 | ### Invoking the Chaincode 100 | 101 | 首先,我们加入`Invoke`函数的signature(签名)。 102 | 103 | ```c 104 | //每个transaction都调用`Invoke`。每个 transactio是'get' 105 | //或者 'set' asset 。 'set'方法可以, 106 | //通过提供一个新的key-value对,创建一个新的asset。 107 | func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response { 108 | 109 | } 110 | ``` 111 | 112 | 与上面的`Init`函数一样,我们需要从`ChaincodeStubInterface`中提取参数。 `Invoke`函数的参数,将是要调用的chaincode应用程序函数的名称。 113 | 114 | 在我们的例子中,我们的应用程序只有两个函数:`set`和`get`,它们允许设置资产的value或检索当前状态。 我们首先调用`ChaincodeStubInterface.GetFunctionAndParameters`来提取该chaincode应用函数的函数名称和参数。 115 | 116 | ```c 117 | func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response { 118 | // 从transaction proposal中抽取 function和 args 119 | fn, args := stub.GetFunctionAndParameters() 120 | 121 | } 122 | ``` 123 | 124 | 接下来,我们将验证函数名称是`set`还是`get`,并调用这些chaincode应用函数,通过shim返回相应的响应。shim.Success或shim.Error函数将响应序列化为gRPC protobuf消息。 125 | 126 | ```c 127 | func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response { 128 | // 从transaction proposal中抽取 function和 args 129 | fn, args := stub.GetFunctionAndParameters() 130 | 131 | var result string 132 | var err error 133 | if fn == "set" { 134 | result, err = set(stub, args) 135 | } else { 136 | result, err = get(stub, args) 137 | } 138 | if err != nil { 139 | return shim.Error(err.Error()) 140 | } 141 | 142 | return shim.Success([]byte(result)) 143 | } 144 | ``` 145 | 146 | ### 实现Chaincode应用程序 147 | 148 | 如前所述,我们的chaincode应用程序实现了,两个可以通过`Invoke`函数调用的函数。 现在我们来实现这些功能。 149 | 150 | 请注意,正如我们上面提到的,为了访问ledger的状态,我们将利用`chaincode shim API`的`ChaincodeStubInterface.PutState`和`ChaincodeStubInterface.GetState`函数。 151 | 152 | ```c 153 | // 在ledger上,存储 asset (both key and value),如果key 存在,会用新的value覆盖. 154 | func set(stub shim.ChaincodeStubInterface, args []string) (string, error) { 155 | if len(args) != 2 { 156 | return "", fmt.Errorf("Incorrect arguments. Expecting a key and a value") 157 | } 158 | 159 | err := stub.PutState(args[0], []byte(args[1])) 160 | if err != nil { 161 | return "", fmt.Errorf("Failed to set asset: %s", args[0]) 162 | } 163 | return args[1], nil 164 | } 165 | 166 | // 获取指定asset key的value 167 | func get(stub shim.ChaincodeStubInterface, args []string) (string, error) { 168 | if len(args) != 1 { 169 | return "", fmt.Errorf("Incorrect arguments. Expecting a key") 170 | } 171 | 172 | value, err := stub.GetState(args[0]) 173 | if err != nil { 174 | return "", fmt.Errorf("Failed to get asset: %s with error: %s", args[0], err) 175 | } 176 | if value == nil { 177 | return "", fmt.Errorf("Asset not found: %s", args[0]) 178 | } 179 | return string(value), nil 180 | } 181 | ``` 182 | ### 完整代码 183 | 184 | 最后, 加入 `main` 函数, 它调用 `shim.Start` 函数。 185 | 186 | ```c 187 | package main 188 | 189 | import ( 190 | "fmt" 191 | 192 | "github.com/hyperledger/fabric/core/chaincode/shim" 193 | "github.com/hyperledger/fabric/protos/peer" 194 | ) 195 | 196 | type SimpleAsset struct { 197 | } 198 | 199 | func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response { 200 | args := stub.GetStringArgs() 201 | if len(args) != 2 { 202 | return shim.Error("Incorrect arguments. Expecting a key and a value") 203 | } 204 | 205 | err := stub.PutState(args[0], []byte(args[1])) 206 | if err != nil { 207 | return shim.Error(fmt.Sprintf("Failed to create asset: %s", args[0])) 208 | } 209 | return shim.Success(nil) 210 | } 211 | 212 | 213 | func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response { 214 | 215 | fn, args := stub.GetFunctionAndParameters() 216 | 217 | var result string 218 | var err error 219 | if fn == "set" { 220 | result, err = set(stub, args) 221 | } else { 222 | result, err = get(stub, args) 223 | } 224 | if err != nil { 225 | return shim.Error(err.Error()) 226 | } 227 | return shim.Success([]byte(result)) 228 | } 229 | 230 | func set(stub shim.ChaincodeStubInterface, args []string) (string, error) { 231 | if len(args) != 2 { 232 | return "", fmt.Errorf("Incorrect arguments. Expecting a key and a value") 233 | } 234 | 235 | err := stub.PutState(args[0], []byte(args[1])) 236 | if err != nil { 237 | return "", fmt.Errorf("Failed to set asset: %s", args[0]) 238 | } 239 | return args[1], nil 240 | } 241 | 242 | func get(stub shim.ChaincodeStubInterface, args []string) (string, error) { 243 | if len(args) != 1 { 244 | return "", fmt.Errorf("Incorrect arguments. Expecting a key") 245 | } 246 | 247 | value, err := stub.GetState(args[0]) 248 | if err != nil { 249 | return "", fmt.Errorf("Failed to get asset: %s with error: %s", args[0], err) 250 | } 251 | if value == nil { 252 | return "", fmt.Errorf("Asset not found: %s", args[0]) 253 | } 254 | return string(value), nil 255 | } 256 | 257 | // main函数,启动container中的chaincode(在实例化的时候)。 258 | main() { 259 | if err := shim.Start(new(SimpleAsset)); err != nil { 260 | fmt.Printf("Error starting SimpleAsset chaincode: %s", err) 261 | } 262 | } 263 | ``` 264 | 265 | ### Building Chaincode 266 | 267 | 现在让我们编译chaincode。 268 | 269 | ```c 270 | go get -u --tags nopkcs11 github.com/hyperledger/fabric/core/chaincode/shim 271 | go build --tags nopkcs11 272 | ``` 273 | 274 | 假设没有错误,可以继续下一步,测试chaincode。 275 | 276 | ### 使用Dev模式测试 277 | 278 | 通常,chaincodes由peer启动和维护。 然而,在“Dev模式”下,链chaincodes由用户构建并启动。 在快速code/build/run/debug周期迭代的开发阶段,此模式非常有用。 279 | 280 | 我们通过利用,为样例dev网络,预先生成的orderer和channel artifacts,来开始“Dev模式”。 因此,用户可以立即跳到编辑chaincode和调用的过程中。 281 | 282 | ## 安装 Hyperledger Fabric Samples 283 | 284 | 如果之前没有做过,请安装Hyperledger Fabric Samples。 285 | 286 | 到`fabric-samples`目录的`chaincode-docker-devmode` 目录下: 287 | 288 | `cd chaincode-docker-devmode` 289 | 290 | ## 下载 Docker images 291 | 292 | 我们需要四个Docker镜像,才能使“dev模式”按照提供的docker compose script运行。 293 | 如果您安装了`fabric-samples` repo clone,并按照说明下载平台特定的二进制文件,那么你应该,已经在本地安装了必要的Docker镜像。 294 | 295 | 使用`docker images`命令,查看本地的Docker Registry。应该如下“ 296 | 297 | ```c 298 | docker images 299 | REPOSITORY TAG IMAGE ID CREATED SIZE 300 | hyperledger/fabric-tools latest b7bfddf508bc About an hour ago 1.46GB 301 | hyperledger/fabric-tools x86_64-1.1.0 b7bfddf508bc About an hour ago 1.46GB 302 | hyperledger/fabric-orderer latest ce0c810df36a About an hour ago 180MB 303 | hyperledger/fabric-orderer x86_64-1.1.0 ce0c810df36a About an hour ago 180MB 304 | hyperledger/fabric-peer latest b023f9be0771 About an hour ago 187MB 305 | hyperledger/fabric-peer x86_64-1.1.0 b023f9be0771 About an hour ago 187MB 306 | hyperledger/fabric-javaenv latest 82098abb1a17 About an hour ago 1.52GB 307 | hyperledger/fabric-javaenv x86_64-1.1.0 82098abb1a17 About an hour ago 1.52GB 308 | hyperledger/fabric-ccenv latest c8b4909d8d46 About an hour ago 1.39GB 309 | hyperledger/fabric-ccenv x86_64-1.1.0 c8b4909d8d46 About an hour ago 1.39GB 310 | ``` 311 | 312 | 现在打开3个终端,并切换到`chaincode-docker-devmode`目录 313 | 314 | 315 | ## Terminal 1 - Start the network 316 | 317 | `docker-compose -f docker-compose-simple.yaml up` 318 | 319 | 上面的命令使用`SingleSampleMSPSolo`orderer profile启动网络,并以“dev模式”启动peer。 320 | 321 | 它还启动了两个额外的容器 - 一个用于chaincode环境,一个用于与chaincode交互的CLI。 创建和加入Channel的命令被嵌入到CLI容器中,因此我们可以立即跳转到chaincode的调用。 322 | 323 | 324 | ## Terminal 2 - Build & start the chaincode 325 | 326 | `docker exec -it chaincode bash` 327 | 328 | 应该看到如下信息: 329 | 330 | ```c 331 | root@d2629980e76b:/opt/gopath/src/chaincode# 332 | ``` 333 | 334 | 现在编译你的chaincode: 335 | 336 | ```c 337 | cd sacc 338 | go build 339 | ``` 340 | 现在运行chaincode: 341 | 342 | ```c 343 | CORE_PEER_ADDRESS=peer:7052 CORE_CHAINCODE_ID_NAME=mycc:0 ./sacc 344 | ``` 345 | 346 | chaincode,以peer和chaincode日志开始,表示与peer注册成功。 请注意,在此阶段chaincode不与任何Channel关联。与Channel的关联,是在后续步骤中使用实例化命令完成的。 347 | 348 | ## Terminal 3 - Use the chaincode 349 | 350 | 尽管你在 `--peer-chaincodedev` 模式中, 也需要install chaincode,这样 life-cycle system chaincode 可以正常通过它的检查。 当在 `--peer-chaincodedev` 模式中,这个需求可以在后续的版本中被移除掉。 351 | 352 | 启动 CLI container 来进行调用 353 | `docker exec -it cli bash` 354 | 355 | ```c 356 | peer chaincode install -p chaincodedev/chaincode/sacc -n mycc -v 0 357 | peer chaincode instantiate -n mycc -v 0 -c '{"Args":["a","10"]}' -C myc 358 | ``` 359 | 360 | 现在发起一个`invoke` 把 “a” 的值改成“20”。 361 | 362 | ```c 363 | peer chaincode invoke -n mycc -c '{"Args":["set", "a", "20"]}' -C myc 364 | ``` 365 | 再查询一下”a“的值 366 | 367 | ```c 368 | peer chaincode query -n mycc -c '{"Args":["query","a"]}' -C myc 369 | ``` 370 | 371 | 372 | ## Chaincode加密 373 | 374 | 在某些情况下,加密与key相关的value(全部加密或者部分加密)可能是有用的。例如,如果一个人的社会安全号码,或地址正在写入ledger,那么你可能不希望这些数据以明文形式出现。 Chaincode加密,通过利用实体扩展实现,该BCCSP包装,执行诸如加密和椭圆曲线数字签名的加密操作。例如,为了加密,Chaincode的调用者通过transient字段传递加密密钥。然后可以将相同的密钥用于随后的查询操作,从而允许对加密的状态值进行适当的解密。 375 | 376 | 有关更多信息和示例,请参阅`fabric/examples`目录中的`Encc`示例。特别注意`utils.go`帮手程序。此实用工具加载Chaincode shim API和实体扩展,并构建样本加密Chaincode,随后利用新类函数(例如,encryptAndPutState&getStateAndDecrypt)。因此,chaincode现在可以结合Get和Put的基本shim API(填充API)以及Encrypt和Decrypt的附加功能。 377 | 378 | 379 | ## 管理用Go编写的chaincode的外部依赖关系 380 | 381 | 如果你的chaincode需要非Go标准库提供的软件包,则需要将这些软件包包含在您的chaincode中。 有许多工具可用于管理这些依赖关系。 以下演示如何使用govendor: 382 | 383 | ```c 384 | govendor init 385 | govendor add + external //添加所有外部包,或者 386 | govendor add github.com/external/pkg //添加特定的外部软件包 387 | ``` 388 | 389 | 这将外部依赖关系导入本地vendor目录。这样 peer chaincode包和peer chaincode安装操作,将会包含与chaincode包相关的代码。 390 | 391 | 392 | -------------------------------------------------------------------------------- /Hyperledger/构建你的第一个区块链网络 .md: -------------------------------------------------------------------------------- 1 | # Building Your First Network 步骤详解介绍 2 | 3 | ## build your first network (BYFN) 包含的内容 4 | 5 | 第一个Hyperledger Fabric network由下面内容组成: 6 | 7 | * 4 个peers,代表2个不同的organizations。 8 | * 1 个orderer 节点。 9 | * 启动1个 container, 执行脚本,将peers加入Channel,部署和实例化Chaincode,并根据部署的Chaincode,驱动transactions的执行。 10 | 11 | ## 准备工作 12 | 1. https://github.com/xiaofateng/knowledge-without-end/blob/master/区块链/Hyperledger/Hyperledger%20Fabric%20源码和镜像下载.md 13 | 2. 修改`fabric-samples/first-network`目录下的`docker-compose-cli.yaml`日志级别为debug 14 | 15 | ```c 16 | cli: 17 | container_name: cli 18 | image: hyperledger/fabric-tools:$IMAGE_TAG 19 | tty: true 20 | stdin_open: true 21 | environment: 22 | - GOPATH=/opt/gopath 23 | - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock 24 | - CORE_LOGGING_LEVEL=DEBUG 25 | #- CORE_LOGGING_LEVEL=INFO 26 | ``` 27 | 28 | ## Crypto Generator 29 | 30 | 我们将使用`cryptogen`工具(这个工具只是在测试的时候使用,线上一般使用Fabric CA等)为我们的各种network entitie(网络实体)生成加密资料(x509证书和签名密钥)。 这些`certificates`(证书)是身份的代表,它们允许在我们的实体,进行通信和transact时进行签名/验证身份。 31 | 32 | `Cryptogen`使用包含网络拓扑的文件 - `crypto-config.yaml`,并允许我们为Organizations和属于这些Organizations的组件生成一组`certificates` and `keys`(证书和密钥)。 33 | 34 | 每个Organizations都配备了一个独特的根证书(`ca-cert`),将特定的组件(peers and orderers)绑定到该Org。 通过为每个Org分配一个唯一的CA证书,我们正在模拟一个典型的网络,其中一个Member将使用自己的Certificate Authority(证书授权)。 Hyperledger Fabric中的Transactions和通信由实体的private key(私钥,存储的文件名是 `keystore`)签名,然后通过public key(公钥,存储的文件名是 `signcerts`)进行验证。 35 | 36 | 你会注意到这个文件中的 `count` 变量。我们用它来指定每个Organization的peers数量; 在我们的案例中,每个Organization有两个peers。 37 | 38 | 在运行该工具之前,让我们快速浏览一下`crypto-config.yaml`的代码片段。 特别注意OrdererOrgs header下的`“Name”`, `“Domain”` 和 `“Specs”`: 39 | 40 | ```c 41 | OrdererOrgs: 42 | #--------------------------------------------------------- 43 | # Orderer 44 | # -------------------------------------------------------- 45 | - Name: Orderer 46 | Domain: example.com 47 | CA: 48 | Country: US 49 | Province: California 50 | Locality: San Francisco 51 | # OrganizationalUnit: Hyperledger Fabric 52 | # StreetAddress: address for org # default nil 53 | # PostalCode: postalCode for org # default nil 54 | # ------------------------------------------------------ 55 | # "Specs" - See PeerOrgs below for complete description 56 | # ----------------------------------------------------- 57 | Specs: 58 | - Hostname: orderer 59 | # ------------------------------------------------------- 60 | # "PeerOrgs" - Definition of organizations managing peer nodes 61 | # ------------------------------------------------------ 62 | PeerOrgs: 63 | # ----------------------------------------------------- 64 | # Org1 65 | # ---------------------------------------------------- 66 | - Name: Org1 67 | Domain: org1.example.com 68 | EnableNodeOUs: true 69 | ``` 70 | 71 | 网络实体的命名约定如下 - `“{{.Hostname}}.{{.Domain}}”`。 因此,使用我们的ordering node作为参考点,我们看到一个名为 - `orderer.example.com`的ordering node,它与`Orderer`的`MSP ID`绑定。 72 | 73 | 运行`cryptogen`工具后,生成的证书和密钥将被保存到名为`crypto-config`的文件夹中。 74 | 75 | ### 执行`cryptogen`命令 76 | 77 | 下面执行命令(假设当前路径是`hyperledger/fabric-samples/first-network`): 78 | 79 | `../../bin/cryptogen generate --config=./crypto-config.yaml` 80 | 81 | 82 | 执行命令后,为各个节点生成的证书如下: 83 | 84 | `hyperledger/fabric-samples/first-network/crypto-config`目录的层级结构如下: 85 | 86 | ![](media/cryconfig01.png) 87 | 88 | 89 | 目录`/hyperledger/fabric-samples/first-network/crypto-config/ordererOrganizations/example.com`的详细结构如下: 90 | 91 | ![](media/crypto-config.png) 92 | 93 | peerOrganizations目录的内容与上面类似。 94 | 95 | 96 | ## Configuration Transaction Generator 97 | 98 | `configtxgen tool` 创建4个配置artifacts: 99 | 100 | * orderer `genesis block` 101 | * channel `configuration transaction` 102 | * 两个 `anchor peer transactions`,每个Org一个。 103 | 104 | orderer block是ordering service的创世区块。 105 | 106 | 在 Channel 创建的时候,channel configuration transaction 文件广播到 orderer(因为orderer负责创建Channel)。 107 | 108 | anchor peer transactions, 指定在这个Channel上,每个Org的Anchor Peer on this channel。 109 | 110 | `Configtxgen`工具使用 - `configtx.yaml`文件 - 它包含示例网络的定义信息。 **有三个成员 - 一个`Orderer Org`(OrdererOrg,Orderer也是一个专门的org)和两个`Peer Orgs`(`Org1`&`Org2`),**每个管理和维护两个peer节点。 **该文件还指定了由两个Peer Orgs组成的联盟 - `SampleConsortium`。(联盟是org组成的)** 111 | 112 | 请特别注意本文件顶部的`“Profiles”`部分。 你会注意到我们有两个独特的headers。 一个用于orderer genesis block - `TwoOrgsOrdererGenesis` 。一个用于我们的Channel - `TwoOrgsChannel`。 113 | 114 | 这些headers非常重要,因为我们将在创建artifacts时将它们作为参数传递给它们。 115 | 116 | 请注意,我们的`SampleConsortium`在system-level profile中定义,然后由我们的channel-level profile引用。 channel存在于一个联盟的范围内,所有联盟必须在整个网络范围内进行界定。 117 | 118 | 119 | 该文件还包含两个值得注意的附加规范。 首先,我们为每个Peer Org指定Anchor peers(`peer0.org1.example.com`&`peer0.org2.example.com`)。 其次,我们指向每个成员的MSP目录的位置,这反过来又允许我们将每个组织的根证书存储在`orderer genesis block`。 这是一个重要的概念。 现在任何与`ordering service`通信的网络实体都可以验证其数字签名。 120 | 121 | ### 执行`configtxgen`命令生成`orderer genesis block` 122 | 123 | 首先指定`configtx.yaml`文件的位置,目前好像不支持在命令中指定。 124 | `export FABRIC_CFG_PATH=$PWD` 125 | 126 | 然后执行命令: 127 | 128 | ```c 129 | ../../bin/configtxgen -profile TwoOrgsOrdererGenesis -outputBlock ./channel-artifacts/genesis.block 130 | ``` 131 | 132 | 生成的`orderer genesis block`存储在了`./channel-artifacts/`目录下。 133 | 134 | ### 执行`configtxgen`命令创建`Channel Configuration Transaction` 135 | 136 | #### 创建 `channel.tx artifact` 137 | 138 | 首先设置CHANNEL_NAME变量,或者在命令中指定 139 | `export CHANNEL_NAME=mychannel` 140 | 141 | 然后执行命令: 142 | 143 | ```c 144 | ../../bin/configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID $CHANNEL_NAME 145 | ``` 146 | 147 | channel.tx artifact 包含了我们sample channel的定义。 148 | 生成的 `channel.tx` 在`./channel-artifacts`目录下面。 149 | 150 | #### 指定Anchor peer 151 | 152 | `export CHANNEL_NAME=mychannel` 153 | 154 | 为 Org1 指定 Anchor peer 155 | 156 | ```c 157 | ../../bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP 158 | ``` 159 | 160 | 为 Org2 指定 Anchor peer 161 | 162 | ```c 163 | ../../bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org2MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org2MSP 164 | ``` 165 | 166 | 最终channel-artifacts目录文件内容如下: 167 | ![](media/channelArtifacts.jpg) 168 | 169 | ## Start the network(启动网络) 170 | 171 | 我们将使用脚本启动我们的网络。 172 | docker-compose 文件引用了 我们之前下载的images, 173 | 并且使用之前生成的`genesis.block`, bootstraps(自举启动)orderer。 174 | 175 | 首先,启动网络的命令: 176 | `docker-compose -f docker-compose-cli.yaml up -d` 177 | 178 | 如果想要查看网络的实时日志,就不要使用 -d 选项。 179 | 180 | 启动后,会有6个 docker containers,如下图: 181 | 182 | ![](media/dockerContainers.jpg) 183 | 184 | CLI container 会坚持闲置1000秒,当它没有后,可以通过下面的命令重启: 185 | `docker start cli` 186 | 187 | 188 | ## 环境变量 189 | 190 | 下文中的,为了`peer0.org1.example.com`的`CLI`命令正常使用,需要先定义下面给出的四个环境变量。 191 | 这些用于`peer0.org1.example.com`的变量,已经被嵌入到`CLI`容器中。因此我们可以在不传递它们的情况下进行操作。 192 | 193 | 但是,如果您想将发出调用给其它peers 或 the orderer,则需要相应地提供这些值。 194 | 检查`docker-compose-base.yaml`: 195 | 196 | ```c 197 | # Environment variables for PEER0 198 | CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp 199 | CORE_PEER_ADDRESS=peer0.org1.example.com:7051 200 | CORE_PEER_LOCALMSPID="Org1MSP" 201 | CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt 202 | ``` 203 | 204 | ## 创建和加入Channel 205 | 206 | 前面,我们在 Create a Channel Configuration Transaction 部分,使用 `configtxgen`工具创建了channel configuration transaction。 你可以重复该过程以创建其它channel configuration transaction,(使用相同或不同的`configtx.yaml`配置文件)。 然后,可以重复本节中定义的流程,在网络中建立其它Channel。 207 | 208 | 我们将使用docker exec命令进入CLI容器: 209 | `docker exec -it cli bash` 210 | 211 | 如果成功,您应该看到以下内容: 212 | `root@0d78bb69300d:/opt/gopath/src/github.com/hyperledger/fabric/peer#` 213 | 214 | 接下来,我们把,在Create a Channel Configuration Transaction部分中,创建的channel configuration transaction artifact(我们称之为`channel.tx`),作为创建Channel请求的一部分,传递给`orderer`。 215 | 216 | 我们用`-c`指定我们的channel名称,用`-f`指定我们的channel configuration transaction。 本文中,它是channel.tx,但是您可以使用不同的名称来挂载自己的configuration transaction。 我们将再次在`CLI`容器中设置`CHANNEL_NAME`环境变量,以便我们不必显式传递此参数: 217 | 218 | ```c 219 | export CHANNEL_NAME=mychannel 220 | # channel.tx 文件,挂载在你的CLI container的channel-artifacts目录中。 221 | # 为了确认 TLS handshake ,我们也传递了 orderer ca-cert 的路径。 222 | 223 | peer channel create -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/channel.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem 224 | ``` 225 | 注意命令中的 -- cafile 参数。它是orderer’s root cert的local path, 允许验证 TLS handshake。 226 | 227 | 228 | 这个命令返回一个genesis block - `` - 我们将用它来加入该Channel。 它包含在channel.tx中指定的配置信息。如果您未修改默认Channel名字,那么该命令将返回一个标题为`mychannel.block`的原型。 229 | 230 | 231 | 现在,让我们把 `peer0.org1.example.com` 加入到 channel中。 232 | 233 | ```c 234 | peer channel join -b mychannel.block 235 | ``` 236 | 237 | 如果要把`peer0.org2.example.com`加入到Channel中,记得把上面的环境变量作相应的修改。然后执行执行同样命令即可: 238 | 239 | ```c 240 | CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp CORE_PEER_ADDRESS=peer0.org2.example.com:7051 CORE_PEER_LOCALMSPID="Org2MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt 241 | peer channel join -b mychannel.block 242 | ``` 243 | 244 | ## 更新anchor peers 245 | 246 | 以下命令是Channel更新,它们将传播到Channel的定义。**实质上,我们在channel’s genesis block的顶部添加额外的配置信息。请注意,我们没有修改生成块,而只是将 delta(增量) 添加到,定义anchor peers的链中。** 247 | 248 | 更新Channel定义以将`Org1`的anchor peer定义为`peer0.org1.example.com`:(与Channel create命令基本一样,只是把create 变为 update) 249 | 250 | ```c 251 | peer channel update -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/Org1MSPanchors.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem 252 | ``` 253 | 254 | 现在更新,将Org2的anchor peer定义为`peer0.org2.example.com`。 255 | 与Org1 peer的peer channel join命令一样,我们需要设置适当的环境变量。 256 | 257 | ```c 258 | CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp CORE_PEER_ADDRESS=peer0.org2.example.com:7051 CORE_PEER_LOCALMSPID="Org2MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt 259 | peer channel update -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/Org2MSPanchors.tx --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem 260 | ``` 261 | 262 | ## 安装并实例化Chaincode 263 | 本文中,我们将利用一个简单的现有的Chaincode。 264 | 265 | 应用程序通过Chaincode与区块链ledger进行交互。 **因此,我们需要在每个将执行并endorse我们的transactions的peer上,安装Chaincode,然后在Channel上实例化Chaincode。** 266 | 267 | 首先,将示例Go或Node.js Chaincode安装到一个peer上。 这些命令将指定的源代码,放在peer的文件系统上。 268 | 269 | 注意,对于每个Chaincode名称和版本,只能安装一个版本的源代码(即不能安装一份代码的多个版本)。 源代码在peer的文件系统中,文件系统的上下文(或者文件名字)是Chaincode名称和版本,它是语言不可知的。 同样,实例化的Chaincode容器将反映peer上已安装的是哪个语言的源码。 270 | 271 | #### Golang 272 | 273 | ```c 274 | # 这个命令安装 Go chaincode 275 | peer chaincode install -n mycc -v 1.0 -p github.com/chaincode/chaincode_example02/go/ 276 | ``` 277 | #### Node.js 278 | 279 | ```c 280 | # 这个命令安装 Node.js chaincode 281 | # 注意,使用 -l 选项指定语言 282 | peer chaincode install -n mycc -v 1.0 -l node -p /opt/gopath/src/github.com/chaincode/chaincode_example02/node/ 283 | ``` 284 | 285 | 接下来,实例化channel上的chaincode。 286 | 这将初始化channel上的chaincode,设置chaincode的endorsement policy,**并为目标peer启动chaincode容器**。 -P参数,指定我们的policy,指定所需的transaction endorsement,以验证此chaincode。 287 | 288 | 在下面的命令中,我们将policy指定为`-P“OR”('Org0MSP.peer','Org1MSP.peer')“`。 289 | 这意味着我们需要来自属于Org1或Org2的peer的“endorsement(认可)”。 290 | 如果我们将语法更改为AND,那么我们需要两个都认可。 291 | 292 | #### Golang 293 | 294 | ```c 295 | peer chaincode instantiate -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C $CHANNEL_NAME -n mycc -v 1.0 -c '{"Args":["init","a", "100", "b","200"]}' -P "OR ('Org1MSP.peer','Org2MSP.peer')" 296 | ``` 297 | #### Node.js 298 | 299 | 注意,Node.js chaincode 的实例化会花费一些时间。命令并没有挂起; 而是在编译镜像时候,安装fabric-shim layer。 300 | 301 | ```c 302 | peer chaincode instantiate -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C $CHANNEL_NAME -n mycc -l node -v 1.0 -c '{"Args":["init","a", "100", "b","200"]}' -P "OR ('Org1MSP.peer','Org2MSP.peer')" 303 | ``` 304 | 305 | ## Query 306 | 307 | 下面查询一下数据,确保 chaincode 实例化成功了,state DB 里面也产生数据了。语法如下: 308 | 309 | ```c 310 | peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}' 311 | ``` 312 | ## Invoke 313 | 现在,从 a 向 b 转账10元。 这个 transaction will cut a new block 并更新 state DB。语法如下: 314 | 315 | ```c 316 | peer chaincode invoke -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C $CHANNEL_NAME -n mycc -c '{"Args":["invoke","a","b","10"]}' 317 | ``` 318 | 再次查询,看转账是否成功: 319 | 320 | ```c 321 | peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}' 322 | ``` 323 | ## 总结 324 | 上面的分部命令,都在`script.sh`中,通过如下命令执行 325 | `./byfn.sh up` 326 | 使用下面的命令,清理网络: 327 | `./byfn.sh down` 328 | 329 | 330 | 直到对该chaincode执行init或transaction - 读取/写入(例如,查询“a”的值),chaincode container才会在peer上被启动。 331 | 332 | 查看 CLI Docker container 日志命令: 333 | `docker logs -f cli` 334 | 335 | 查看chaincode日志命令,需要单独查看每个 chaincode container : 336 | 337 | ```c 338 | docker logs dev-peer0.org2.example.com-mycc-1.0 339 | 340 | 下面是返回的具体日志: 341 | 04:30:45.947 [BCCSP_FACTORY] DEBU : Initialize BCCSP [SW] 342 | ex02 Init 343 | Aval = 100, Bval = 200 344 | ``` 345 | 346 | 欢迎加入区块链技术交流QQ群 694125199 347 | 348 | 更多区块链知识: 349 | https://github.com/xiaofateng/knowledge-without-end/tree/master/区块链/Hyperledger 350 | 351 | 本文参考: 352 | http://hyperledger-fabric.readthedocs.io/en/latest/build_network.html#create-join-channel 353 | 354 | 355 | -------------------------------------------------------------------------------- /Hyperledger/部署维护Chaincode.md: -------------------------------------------------------------------------------- 1 | # -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Readme 2 | **大数据,机器学习,区块链学习笔记** 3 | -------------------------------------------------------------------------------- /Spark/SparkStreaming和Kafka的整合方式 .md: -------------------------------------------------------------------------------- 1 | # 基于Receiver 方式 2 | 这个receiver是基于 Kafka high-level consumer API实现的。像其它的receivers一样,接收到的数据会放到spark的executor里面,然后sparkstreaming程序启动任务处理数据。 3 | 4 | # 直接方法,没有receiver 5 | 6 | 这个方法是spark1.3引进的,现在都是spark2.0版本了,看样会一直延续下去了。这个的引入是为了保证端对端的可靠性保证。 7 | 8 | 这个方法定期的从 kafka的每个topic+partition里面查询最新offset的数据,然后相应的,定义每个batch里面处理的数据offset范围。 9 | 当处理数据的job启动的时候,Kafka的simple consumer api读取确定好的kafka中的offset范围(就是从文件系统中读文件) 10 | 11 | 这种方法,与第一种方法相比的优势: 12 | * (一)简单的并行度 13 | sparkstreaming会创建和kafka分区一样多的RDD分区来消费,这样的话,所有的节点都会从kafka并行的读取数据。 14 | Kafka分区和RDD分区是一对一的关系,这样更便于理解和优化。 15 | * (二)高效 16 | 第一种方法,为了达到零数据丢失,要写WAL,这样数据存了两份,一份在kafka,一份在WAL中,这是不高效的。第二种方法,不需要receiver,因此不需要WAL, 17 | 只要kafka中有足够的消息,消息就能从kafka中恢复处理。 18 | * (三)Exactly Once语义 19 | 第一种方法,使用kafka的high level api,将消费的offset存储在zookeeper中。这种方法可以保证数据不会丢失,但是数据可能会被重复消费,因为spark真正接收到的数据可能和zookeeper中的offset不一致。 20 | 第二种方法,使用simple kafka api,不使用zookeeper,offset是通过sparkstreaming的checkpoint来记录的,这样就消除了sparkstreaming和kafka(或者zookeeper)之间的不一致性,此时,sparkstreaming不会丢失数据。如何保证 Exactly Once呢 21 | (1)幂等操作 22 | (2)业务代码添加事物操作 23 | 如果业务上不能幂等操作,只能通过业务自身的逻辑代码来解决了。 24 | 25 | 官方方案: 26 | 27 | ```scala 28 | dstream.foreachRDD { (rdd, time) => 29 | rdd.foreachPartition { partitionIterator => 30 | val partitionId = TaskContext.get.partitionId() 31 | val uniqueId = generateUniqueId(time.milliseconds, partitionId) 32 | // use this uniqueId to transactionally commit the data in partitionIterator 33 | } 34 | } 35 | ``` 36 | 使用batch time和partition index来创建一个id,使用这个id来确保数据的唯一性 37 | 启动事务并使用这个id来更新外部系统数据,如果这个id不存在则提交更新,如果这个id已经存在那么则放弃更新。 38 | 39 | 注意,第二种方法,不会把offset写到zookeeper中,所以,基于zookeeper的kafka监控工具都失效了,然而,你可以在每个batch中访问offset,更新到zookeeper中。 40 | 41 | **第二种方法使用checkpoint保存数据,当程序代码改变时,会产生问题,解决方法是offset保存在zookeeper中。 42 | ** 43 | 当你启动程序消费kafka的数据的时候,默认情况下,第二种方法会从最近的offset开始消费,你也可以通过auto.offeset.reset设置成 smallest,这样它就会从最小的offset开始消费。 44 | 与第二种方法相关的配置是saprk.streaming.kafka.* 45 | 重要的一个配置是,spark.streaming.kafka.maxRatePerPartition,表示,在每个kafka 分区中,被direct api读取的数据的最大速度。 46 | 在spark-submit的时候 –conf 指定这个配置。 47 | 48 | -------------------------------------------------------------------------------- /Spark/Spark入门三部曲之第一步Spark基础知识 .md: -------------------------------------------------------------------------------- 1 | #Spark入门三部曲之第一步Spark基础知识 2 | Spark运行环境 3 | 4 | Spark 是Scala写的, 运行在JVM上。所以运行环境是Java6或者以上。 5 | 如果想要使用 Python API,需要安装Python 解释器2.6版本或者以上。 6 | 目前Spark(1.2.0版本) 与Python 3不兼容。 7 | Spark下载 8 | 9 | 下载地址:http://spark.apache.org/downloads.html,选择Pre-built for Hadoop 2.4 and later 这个包,点击直接下载,这会下载一个spark-1.2.0-bin-hadoop2.4.tgz的压缩包 10 | 搭建Spark不需要Hadoop,如果你有hadoop集群或者hdfs,你可以下载相应的版本。 11 | 解压:tar -zxvf spark-1.2.0-bin-hadoop2.4.tgz 12 | Spark的Shells 13 | 14 | Spark的shell使你能够处理分布在集群上的数据(这些数据可以是分布在硬盘上或者内存中)。 15 | Spark可以把数据加载到工作节点的内存中,因此,许多分布式处理(甚至是分布式的1T数据的处理)都可以在几秒内完成。 16 | 上面的特性,使迭代式计算,实时查询、分析一般能够在shells中完成。Spark提供了Python shells和 Scala shells。 17 | 打开Spark的Scala Shell: 18 | 19 | 到Spark目录bin/pysparkbin/spark-shell打开Scala版本的shell 20 | 21 | 22 | 例子: 23 | 24 | ```scala 25 | scala> val lines = sc.textFile(“../../testfile/helloSpark”) // 创建一个叫lines的RDD 26 | lines: org.apache.spark.rdd.RDD[String] = ../../testfile/helloSpark MappedRDD[1] at textFile at :12 27 | scala> lines.count() // 对这个RDD中的行数进行计数 28 | res0: Long = 2 29 | scala> lines.first() // 文件中的第一行 30 | res1: String = hello spark 31 | ``` 32 | 33 | 修改日志级别:conf/log4j.properties log4j.rootCategory=WARN, console 34 | 35 | 36 | 37 | Spark的核心概念 38 | 39 | Driver program: 40 | 41 | 包含程序的main()方法,RDDs的定义和操作。(在上面的例子中,driver program就是Spark Shell它本身了) 42 | 它管理很多节点,我们称作executors。 43 | count()操作解释(每个executor计算文件的一部分,最后合并)。 44 | SparkContext: 45 | Driver programs 通过一个 SparkContext 对象访问 Spark,SparkContext 对象代表和一个集群的连接。 46 | 在Shell中SparkContext 自动创建好了,就是sc, 47 | 48 | 49 | RDDs: 50 | 在Spark中,我们通过分布式集合(distributed collections,也就是RDDs)来进行计算,这些分布式集合,并行的分布在整个集群中。 51 | RDDs 是 Spark分发数据和计算的基础抽象类。 52 | 用SparkContext创建RDDs 53 | 上面例子中使用sc.textFile()创建了一个RDD,叫lines,它是从我们的本机文本文件中创建的,这个RDD代表了一个文本文件的每一行。我们可以在RDD上面进行各种并行化的操作,例如计算数据集中元素的个数或者打印出第一行。 54 | 55 | Spark PPT 下载地址:http://pan.baidu.com/s/1i3IkdoD 56 | 57 | 58 | -------------------------------------------------------------------------------- /Spark/Spark入门三部曲之第三步Spark程序的开发和运行及WordCount.md: -------------------------------------------------------------------------------- 1 | # 2 | 编写wordcount程序 3 | 4 | 手动导入包:import org.apache.spark.SparkContext._ 5 | 6 | ``` 7 | val conf = new SparkConf().setAppName(“wordCount”)// 创建一个Spark Context. 8 | val sc = new SparkContext(conf) 9 | val input = sc.textFile(“/home/spark/testfile/helloSpark”)// 加载数据 10 | val words = input.flatMap(line => line.split(” “))// 把每一行分割成单词 11 | val counts = words.map(word => (word, 1)).reduceByKey{case (x, y) => x + y}//转换成pairs 并且计数 12 | counts.saveAsTextFile(“/home/spark/testfileResult/wordCountRes”)// 保存动作。 13 | ``` 14 | 15 | 16 | 打包: 17 | 18 | build->build artifacts->build 19 | 20 | 打成jar包,将jar包上传至spark集群上。 21 | 22 | 启动集群: 23 | 24 | 25 | ``` 26 | 启动master 27 | ./sbin/start-master.sh 28 | 启动worker 29 | ./bin/spark-class org.apache.spark.deploy.worker.Worker spark://ubuntu:7077 30 | 提交作业 31 | ./bin/spark-submit –master spark://ubuntu:7077 –class HelloSpark /home/spark/testjar/hellosbt.jar 32 | ``` 33 | 34 | 提交后,可以在下面的ui上看作业的运行。 35 | 36 | Spar job UI http://localhost:4040/ 37 | master的UI http://localhost:8080/ 38 | 39 | 如果,有不清楚的地方,可以看我录制的spark入门视频,完全免费, 40 | 41 | 视频地址:https://pan.baidu.com/s/1c4ba8dq 42 | 43 | -------------------------------------------------------------------------------- /Spark/Spark入门三部曲之第二步Spark开发环境搭建.md: -------------------------------------------------------------------------------- 1 | # 使用Scala+IntelliJ IDEA+Sbt搭建开发环境 2 | 3 | 提示 4 | 5 | 搭建开发环境常遇到的问题: 6 | 7 | 1.网络问题,导致sbt插件下载失败,解决方法,找到一个好的网络环境, 8 | 9 | 或者预先从我提供的网盘中下载jar(链接:http://pan.baidu.com/s/1qWFSTze 密码:lszc) 10 | 11 | 将下载的.ivy2压缩文件,解压后,放到你的用户目录下。 12 | 13 | 14 | 2.版本匹配问题,版本不匹配会遇到各种问题,解决方法,按照如下版本搭建, 15 | 16 | scala(2.10.3),sbt(0.13),sbt-assembly(0.11.2),spark(1.2.0) 17 | 18 | 3.如果按照本教程 搭建仍不成功,推荐看www.bigdatastudy.cn上我录制的课程Spark开发环境搭建(免费的) 19 | 20 | 安装Scala 21 | 22 | scala下载地址:http://www.scala-lang.org/download/2.10.3.html 23 | 24 | 本教程中,相关软件下载网盘地址(链接:http://pan.baidu.com/s/1qWFSTze 密码:lszc) 25 | 26 | 默认安装选项会自动配置环境变量。 27 | 28 | 如果没有自动配置,进行环境变量配置 29 | 30 | SCALA_HOME: C:\Program Files (x86)\scala\ 31 | 32 | Path后面加上 ;%SCALA_HOME%\bin 33 | 34 | 35 | 36 | IntelliJ IDEA的下载,安装 37 | 38 | 下载地址:https://www.jetbrains.com/idea/ 39 | 40 | 激活码如下: 41 | 42 | key:tommy 43 | value:49164-YPNVL-OXUZL-XIWM4-Z9OHC-LF053 44 | 45 | key:itey 46 | value:91758-T1CLA-C64F3-T7X5R-A7YDO-CRSN1 47 | 48 | 49 | 50 | IntelliJ IDEA常用的设置 51 | 52 | 在IntellJ/bin/idea64.exe.vmoptions(64位,物理内存大,建议增大),加大IDEA的启动内存: 53 | -Xms512m 54 | -Xmx1024m 55 | -XX:MaxPermSize=512m 56 | 57 | 主题和颜色: 58 | Settings – IDE Settings – Appearance – Theme:Darcula 59 | 然后把下面override font选项勾上,选择Yahei 14号字体。 60 | 61 | 编辑器界面字体设置: 62 | 可以在Editor – Colors&Fonts – Fonts另存为一个新的主题,并在这个新主题中修改配置。 63 | 64 | 光标所在行背景颜色: 65 | Editor – Colors&Fonts – General – Caret row,选择蓝色背景,以便具有较大色差。 66 | 67 | 68 | 69 | 为每个项目指定不同版本的JDK: 70 | 71 | IDEA可以为每个项目指定不同版本的JDK,并且需要开发者手动配置项目的所使用的JDK版本。配置方法如下: 72 | 单击File | Project Structure菜单项,打开ProjectStructure对话框; 73 | 在左侧列表框中,选择SDKs列表项,进入SDK配置页面; 74 | 若中间的SDK列表框没有选项,则单击“+”号创建一个JDK列表项; 75 | 选择JDK列表项,在SDK ’JDK’选项卡页面中,单击JDK home path项目的浏览按钮,定位JDK安装路径并保存 76 | 77 | 78 | 79 | 插件安装 80 | File>Settings>Plugins,搜索Scala直接安装,安装完后会提示重新启动。这个插件中有scala和sbt。 81 | 无需再单独下载sbt插件。 82 | 83 | 84 | build.sbt文件 85 | 使用bt 0.13进行编译 86 | 87 | build.sbt文件内容如下: 88 | //导入支持编译成jar包的一些函数(这些函数是sbt-assembly插件中的) 89 | 90 | ``` 91 | import AssemblyKeys._ 92 | name := “SparkApp” 93 | version := “1.0” 94 | scalaVersion := “2.10.3” 95 | libraryDependencies ++= Seq(// Spark dependency 96 | “org.apache.spark” % “spark-core_2.10″ % “1.2.0” % “provided”, 97 | “net.sf.jopt-simple” % “jopt-simple” % “4.3”, 98 | “joda-time” % “joda-time” % “2.0”) 99 | 100 | //该声明包括assembly plug-in功能 101 | assemblySettings 102 | // 使用 assembly plug-in配置jar 103 | jarName in assembly := “my-project-assembly.jar” 104 | // 从我们的assembly JAR中排除Scala, 因为Spark已经绑定了Scala 105 | assemblyOption in assembly :=(assemblyOption in assembly).value.copy(includeScala = false) 106 | 107 | ``` 108 | 109 | 进一步配置 110 | 111 | 要使sbt-assembly插件生效,在project/目录下新建一个文件,列出这个插件的依赖。 112 | 113 | 新建project/assembly.sbt 增加如下的配置: 114 | 115 | addSbtPlugin(“com.eed3si9n” % “sbt-assembly” % “0.11.2”) 116 | 117 | 118 | 自此,环境搭建完毕。 119 | 120 | spark的安装,请参考Spark入门三部曲之第一步Spark的安装 121 | 122 | Spark程序的开发和运行,请参考Spark入门三部曲之第三步Spark程序的开发和运行 123 | 124 | --------------------------------------------------------------------------------