diff --git a/C_Universal_Brockchain/12.md b/C_Universal_Brockchain/12.md deleted file mode 100644 index e69de29b..00000000 diff --git a/C_Universal_Brockchain/13.md b/C_Universal_Brockchain/13.md deleted file mode 100644 index e69de29b..00000000 diff --git a/C_Universal_Brockchain/14.md b/C_Universal_Brockchain/14.md deleted file mode 100644 index e69de29b..00000000 diff --git a/C_Universal_Brockchain/15.md b/C_Universal_Brockchain/15.md deleted file mode 100644 index e69de29b..00000000 diff --git a/C_Universal_Brockchain/16.md b/C_Universal_Brockchain/16.md deleted file mode 100644 index e69de29b..00000000 diff --git a/C_Universal_Brockchain/17.md b/C_Universal_Brockchain/17.md deleted file mode 100644 index e69de29b..00000000 diff --git a/C_Universal_Brockchain/18.md b/C_Universal_Brockchain/18.md deleted file mode 100644 index e69de29b..00000000 diff --git a/C_Universal_Brockchain/19.md b/C_Universal_Brockchain/19.md deleted file mode 100644 index e69de29b..00000000 diff --git a/C_Universal_Brockchain/20.md b/C_Universal_Brockchain/20.md deleted file mode 100644 index e69de29b..00000000 diff --git a/C_Universal_Brockchain/21.md b/C_Universal_Brockchain/21.md deleted file mode 100644 index e69de29b..00000000 diff --git a/C_Universal_Brockchain/22.md b/C_Universal_Brockchain/22.md deleted file mode 100644 index e69de29b..00000000 diff --git a/C_Universal_Brockchain/23.md b/C_Universal_Brockchain/23.md deleted file mode 100644 index e69de29b..00000000 diff --git a/C_Universal_Brockchain/README.md b/C_Universal_Brockchain/README.md index 8e4aae58..54d8e6af 100644 --- a/C_Universal_Brockchain/README.md +++ b/C_Universal_Brockchain/README.md @@ -18,110 +18,110 @@ ## 目标? -> 1. 在前面的学习,我是按照通过完成某一个目标来进行学习的,包括编程,便是为了实现某一个项目而去学习未知的知识 -> 2. 可能在我们学习中有很多东西我讲的不是很周全,或者是听的不是很懂,我是这样想的,前期找到框架,抽象思维是特别难的,包括刚接触区块链,建议先将项目跑通,或许我们慢慢就明白了其中的原理 +> 1. 在前面的学习,我是按照通过完成某一个目标来进行学习的,包括编程,便是为了实现某一个项目而去学习未知的知识。 +> 2. 可能在我们学习中有很多东西我讲的不是很周全,或者是听的不是很懂,我是这样想的,前期找到框架,抽象思维是特别难的,包括刚接触区块链,建议先将项目跑通,或许我们慢慢就明白了其中的原理。 ## 目录 -#### 1. [C-Universal Brockchain(CNB)开发指南](1.md) +#### 1. [C-Universal Brockchain(CNB)开发指南](markdown/1.md) -#### 2. [fabric项目整体构架设计、开发、部署](2.md) +#### 2. [fabric项目整体构架设计、开发、部署](markdown/2.md) -#### 3. [IPFS白皮书 中文版](3.md) +#### 3. [IPFS白皮书 中文版](markdown/3.md) -#### 4. [fabric超级账本](4.md) +#### 4. [fabric超级账本](markdown/4.md) -#### 5. [hyperledger白皮书](5.md) +#### 5. [hyperledger白皮书](markdown/5.md) -#### 6. [MVC 模式](6.md) +#### 6. [MVC 模式](markdown/6.md) -#### 7. [超级账本词汇表](7.md) +#### 7. [超级账本词汇表](markdown/7.md) -#### 8. [hyperledge fabric 环境搭建](8.md) +#### 8. [hyperledge fabric 环境搭建](markdown/8.md) -#### 9. [构建fabric的网络环境](9.md) +#### 9. [构建fabric的网络环境](markdown/9.md) -#### 10. [](10.md) +#### 10. [fabric以及和以太坊区别](markdown/10.md) -#### 11. [](11.md) +#### 11. [双重支付](markdown/11.md) -#### 12. [](12.md) +#### 12. [区块链的定义与原理](markdown/12.md) -#### 13. [](13.md) +#### 13. [](markdown/13.md) -#### 14. [](14.md) +#### 14. [](markdown/14.md) -#### 15. [](15.md) +#### 15. [](markdown/15.md) -#### 16. [](16.md) +#### 16. [](markdown/16.md) -#### 17. [](17.md) +#### 17. [](markdown/17.md) -#### 18. [](18.md) +#### 18. [](markdown/18.md) -#### 19. [](19.md) +#### 19. [](markdown/19.md) -#### 20. [](20.md) +#### 20. [](markdown/20.md) -#### 21. [](21.md) +#### 21. [](markdown/21.md) -#### 22. [](22.md) +#### 22. [](markdown/22.md) -#### 23. [](23.md) +#### 23. [](markdown/23.md) -#### 24. [](24.md) +#### 24. [](markdown/24.md) -#### 25. [](25.md) +#### 25. [](markdown/25.md) -#### 26. [](26.md) +#### 26. [](markdown/26.md) -#### 27. [](27.md) +#### 27. [](markdown/27.md) -#### 28. [](28.md) +#### 28. [](markdown/28.md) -#### 29. [](29.md) +#### 29. [](markdown/29.md) -#### 30. [](30.md) +#### 30. [](markdown/30.md) -#### 31. [](31.md) +#### 31. [](markdown/31.md) -#### 32. [](32.md) +#### 32. [](markdown/32.md) -#### 33. [](33.md) +#### 33. [](markdown/33.md) -#### 34. [](34.md) +#### 34. [](markdown/34.md) -#### 35. [](35.md) +#### 35. [](markdown/35.md) -#### 36. [](36.md) +#### 36. [](markdown/36.md) -#### 37. [](37.md) +#### 37. [](markdown/37.md) -#### 38. [](38.md) +#### 38. [](markdown/38.md) -#### 39. [](39.md) +#### 39. [](markdown/39.md) -#### 40. [](40.md) +#### 40. [](markdown/40.md) -#### 41. [](41.md) +#### 41. [](markdown/41.md) -#### 42. [](42.md) +#### 42. [](markdown/42.md) -#### 43. [](43.md) +#### 43. [](markdown/43.md) -#### 44. [](44.md) +#### 44. [](markdown/44.md) -#### 45. [](45.md) +#### 45. [](markdown/45.md) -#### 46. [](46.md) +#### 46. [](markdown/46.md) -#### 47. [](47.md) +#### 47. [](markdown/47.md) -#### 48. [](48.md) +#### 48. [](markdown/48.md) -#### 49. [](49.md) +#### 49. [](markdown/49.md) -#### 50. [](50.md) +#### 50. [](markdown/50.md) @@ -131,15 +131,15 @@ ## Golang学习 -😶‍🌫️[go语言官方编程指南](https://golang.org/#) +😶‍🌫️[go语言官方编程指南](markdown/https://golang.org/#) > go语言的官方文档学习笔记很全,推荐可以去官网学习–了解 ⏬⏬⏬⏬⏬**理解一个事物最好的方式就是把问题当作机器来层层解析**⏬⏬⏬⏬⏬ -## [🖱️点击进入Go语言学习](https://github.com/3293172751/Block_Chain/blob/master/TOC.md) +## [🖱️点击进入Go语言学习](markdown/https://github.com/3293172751/Block_Chain/blob/master/TOC.md) -## [🖱️Go语言路线](https://github.com/3293172751/Block_Chain/blob/master/go_route.md) +## [🖱️Go语言路线](markdown/https://github.com/3293172751/Block_Chain/blob/master/go_route.md) ------ @@ -149,9 +149,9 @@ 以太坊和比特币一样,底层框架都是区块链协议,区块链本质上是一个应用了密码学技术的分布式数据库系统。建议看一下**以太坊白皮书(需要有golang编程基础)** -[![img](https://camo.githubusercontent.com/a411fbf80ed8b270ce46ab6f188b1d8468bb67d9001e7bebbbedbe0ee36b585f/68747470733a2f2f73322e6c6f6c692e6e65742f323032322f30332f32302f67546944645541787448474a3466382e706e67)](https://etherscan.io/) +[![img](markdown/https://camo.githubusercontent.com/a411fbf80ed8b270ce46ab6f188b1d8468bb67d9001e7bebbbedbe0ee36b585f/68747470733a2f2f73322e6c6f6c692e6e65742f323032322f30332f32302f67546944645541787448474a3466382e706e67)](markdown/https://etherscan.io/) -## [🖱️点击进入ETH学习](https://github.com/3293172751/Block_Chain/blob/master/eth/TOC.md) +## [🖱️点击进入ETH学习](markdown/https://github.com/3293172751/Block_Chain/blob/master/eth/TOC.md) ------ @@ -163,7 +163,7 @@ > > ——张首晟 -## [🖱️区块链工程师路线](https://github.com/3293172751/Block_Chain/blob/master/route.md) +## [🖱️区块链工程师路线](markdown/https://github.com/3293172751/Block_Chain/blob/master/route.md) -- [区块链公益项目(NFT+私链/联盟链/私链)](https://github.com/3293172751/Block_Chain/blob/master/区块链公益项目) -- [共识算法](https://github.com/3293172751/Block_Chain/blob/master/blockchain/README.md) \ No newline at end of file +- [区块链公益项目(NFT+私链/联盟链/私链)](markdown/https://github.com/3293172751/Block_Chain/blob/master/区块链公益项目) +- [共识算法](markdown/https://github.com/3293172751/Block_Chain/blob/master/blockchain/README.md) \ No newline at end of file diff --git a/C_Universal_Brockchain/assets/aOjRI78PE93KhJT.png b/C_Universal_Brockchain/assets/aOjRI78PE93KhJT.png new file mode 100644 index 00000000..ebc54690 Binary files /dev/null and b/C_Universal_Brockchain/assets/aOjRI78PE93KhJT.png differ diff --git a/C_Universal_Brockchain/image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMwMTEwNDM1,size_16,color_FFFFFF,t_70-16537372953451.png b/C_Universal_Brockchain/image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMwMTEwNDM1,size_16,color_FFFFFF,t_70-16537372953451.png new file mode 100644 index 00000000..6f803336 Binary files /dev/null and b/C_Universal_Brockchain/image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMwMTEwNDM1,size_16,color_FFFFFF,t_70-16537372953451.png differ diff --git a/C_Universal_Brockchain/image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMwMTEwNDM1,size_16,color_FFFFFF,t_70-16537372953452.png b/C_Universal_Brockchain/image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMwMTEwNDM1,size_16,color_FFFFFF,t_70-16537372953452.png new file mode 100644 index 00000000..0b645c03 Binary files /dev/null and b/C_Universal_Brockchain/image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMwMTEwNDM1,size_16,color_FFFFFF,t_70-16537372953452.png differ diff --git a/C_Universal_Brockchain/image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMwMTEwNDM1,size_16,color_FFFFFF,t_70-16537372953453.png b/C_Universal_Brockchain/image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMwMTEwNDM1,size_16,color_FFFFFF,t_70-16537372953453.png new file mode 100644 index 00000000..ebfe0f7c Binary files /dev/null and b/C_Universal_Brockchain/image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMwMTEwNDM1,size_16,color_FFFFFF,t_70-16537372953453.png differ diff --git a/C_Universal_Brockchain/image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMwMTEwNDM1,size_16,color_FFFFFF,t_70.png b/C_Universal_Brockchain/image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMwMTEwNDM1,size_16,color_FFFFFF,t_70.png new file mode 100644 index 00000000..45f6f474 Binary files /dev/null and b/C_Universal_Brockchain/image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMwMTEwNDM1,size_16,color_FFFFFF,t_70.png differ diff --git a/C_Universal_Brockchain/image/zqGbr4vjloYXSQ6.png b/C_Universal_Brockchain/image/zqGbr4vjloYXSQ6.png new file mode 100644 index 00000000..7ea99631 Binary files /dev/null and b/C_Universal_Brockchain/image/zqGbr4vjloYXSQ6.png differ diff --git a/C_Universal_Brockchain/1.md b/C_Universal_Brockchain/markdown/1.md similarity index 100% rename from C_Universal_Brockchain/1.md rename to C_Universal_Brockchain/markdown/1.md diff --git a/C_Universal_Brockchain/10.md b/C_Universal_Brockchain/markdown/10.md similarity index 99% rename from C_Universal_Brockchain/10.md rename to C_Universal_Brockchain/markdown/10.md index 10c937ed..ddb8ce3f 100644 --- a/C_Universal_Brockchain/10.md +++ b/C_Universal_Brockchain/markdown/10.md @@ -1,5 +1,7 @@ # fabric以及和以太坊区别 +[toc] + ### 以太坊 + “链” 是信任的描点,所有的信任都来自于链。 diff --git a/C_Universal_Brockchain/100.md b/C_Universal_Brockchain/markdown/100.md similarity index 100% rename from C_Universal_Brockchain/100.md rename to C_Universal_Brockchain/markdown/100.md diff --git a/C_Universal_Brockchain/11.md b/C_Universal_Brockchain/markdown/11.md similarity index 100% rename from C_Universal_Brockchain/11.md rename to C_Universal_Brockchain/markdown/11.md diff --git a/C_Universal_Brockchain/markdown/12.md b/C_Universal_Brockchain/markdown/12.md new file mode 100644 index 00000000..efe09cab --- /dev/null +++ b/C_Universal_Brockchain/markdown/12.md @@ -0,0 +1,67 @@ +> 为什么要推荐学git,不仅仅因为技术上的实现,合作。还有的是git和区块链有很多相似的地方 + +# 定义与原理 + +[toc] + +# 定义 + +区块链技术自身仍然在飞速发展中,相关规范和标准还待进一步成熟。 + +公认的最早关于区块链的描述性文献是中本聪所撰写的 [《比特币:一种点对点的电子现金系统》](https://bitcoin.org/bitcoin.pdf),但该文献重点在于讨论比特币系统,并没有明确提出区块链的术语。在其中,区块和链被描述为用于记录比特币交易账目历史的数据结构。 + +另外,[Wikipedia](https://en.wikipedia.org/wiki/Blockchain) 上给出的定义中,将区块链类比为一种分布式数据库技术,通过维护数据块的链式结构,可以维持持续增长的、不可篡改的数据记录。 + +笔者认为,讨论区块链可以从狭义和广义两个层面来看待。 + +狭义上,区块链是一种以区块为基本单位的链式数据结构,区块中利用数字摘要对之前的交易历史进行校验,适合分布式记账场景下防篡改和可扩展性的需求。 + +广义上,区块链还指代基于区块链结构实现的分布式记账技术,包括分布式共识、隐私与安全保护、点对点通信技术、网络协议、智能合约等。 + +# 早期应用 + +1990 年 8 月,Bellcore(1984 年由 AT&T 拆分而来的研究机构)的 Stuart Haber 和 W. Scott Stornetta 在论文《How to Time-Stamp a Digital Document》中就提出利用链式结构来解决防篡改问题,其中新生成的时间证明需要包括之前证明的 Hash 值。这可以被认为是区块链结构的最早雏形。 + +后来,2005 年 7 月,在 Git 等开源软件中,也使用了类似区块链结构的机制来记录提交历史。 + +区块链结构最早的大规模应用出现在 2009 年初上线的比特币项目中。在无集中式管理的情况下,比特币网络持续稳定,支持了海量的交易记录,并且从未出现严重的漏洞,引发了广泛关注。这些都与区块链结构自身强校验的特性密切相关。 + +# 基本原理 + +区块链的基本原理理解起来并不复杂。首先来看三个基本概念: + +- 交易(Transaction):一次对账本的操作,导致账本状态的一次改变,如添加一条转账记录; + +- 区块(Block):记录一段时间内发生的所有交易和状态结果等,是对当前账本状态的一次共识; + +- 链(Chain):由区块按照发生顺序串联而成,是整个账本状态变化的日志记录。 + +如果把区块链系统作为一个状态机,则每次交易意味着一次状态改变;生成的区块,就是参与者对其中交易导致状态改变结果的共识。 + +区块链的目标是实现一个分布的数据记录账本,这个账本只允许添加、不允许删除。账本底层的基本结构是一个线性的链表。链表由一个个“区块”串联组成(如下图所示),后继区块中记录前导区块的哈希(Hash)值。某个区块(以及块里的交易)是否合法,可通过计算哈希值的方式进行快速检验。网络中节点可以提议添加一个新的区块,但必须经过共识机制来对区块达成确认。 + +![image-20220528174218852](C:\Users\smile\Desktop\区块链\C_Universal_Brockchain\image\zqGbr4vjloYXSQ6.png) + +> 区块链结构示例 + + + +# 以比特币为例理解区块链工作过程 + +具体以比特币网络为例,来看其中如何使用了区块链技术。 + +首先,用户通过比特币客户端发起一项交易,消息广播到比特币网络中等待确认。网络中的节点会将收到的等待确认的交易请求打包在一起,添加上前一个区块头部的哈希值等信息,组成一个区块结构。然后,试图找到一个 nonce 串(随机串)放到区块里,使得区块结构的哈希结果满足一定条件(比如小于某个值)。这个计算 nonce 串的过程,即俗称的“挖矿”。nonce 串的查找需要花费一定的计算力。 + +一旦节点找到了满足条件的 nonce 串,这个区块在格式上就“合法”了,成为候选区块。节点将其在网络中广播出去。其它节点收到候选区块后进行验证,发现确实合法,就承认这个区块是一个新的合法区块,并添加到自己维护的本地区块链结构上。当大部分节点都接受了该区块后,意味着区块被网络接受,区块中所包括的交易也就得到确认。 + +这里比较关键的步骤有两个,一个是完成对一批交易的共识(创建合法区块结构);一个是新的区块添加到链结构上,被网络认可,确保未来无法被篡改。当然,在实现上还会有很多额外的细节。 + +比特币的这种基于算力(寻找 nonce 串)的共识机制被称为工作量证明(Proof of Work,PoW)。这是因为要让哈希结果满足一定条件,并无已知的快速启发式算法,只能对 nonce 值进行逐个尝试的蛮力计算。尝试的次数越多(工作量越大),算出来的概率越大。 + +通过调节对哈希结果的限制条件,比特币网络控制平均约 10 分钟产生一个合法区块。算出区块的节点将得到区块中所有交易的管理费和协议固定发放的奖励费(目前是 12.5 比特币,每四年减半)。 + +读者可能会关心,比特币网络是任何人都可以加入的,如果网络中存在恶意节点,能否进行恶意操作来对区块链中记录进行篡改,从而破坏整个比特币网络系统。比如最简单的,故意不承认别人产生的合法候选区块,或者干脆拒绝来自其它节点的交易请求等。 + +实际上,因为比特币网络中存在大量(据估计数千个)的维护节点,而且大部分节点都是正常工作的,默认都只承认所看到的最长的链结构。只要网络中不存在超过一半的节点提前勾结一起采取恶意行动,则最长的链将很大概率上成为最终合法的链。而且随着时间增加,这个概率会越来越大。例如,经过 6 个区块生成后,即便有一半的节点联合起来想颠覆被确认的结果,其概率也仅为 (1/2)^6 ≈ 1.6%,即低于 1/60 的可能性。10 个区块后概率将降到千分之一以下。 + +当然,如果整个网络中大多数的节点都联合起来作恶,可以导致整个系统无法正常工作。要做到这一点,往往意味着付出很大的代价,跟通过作恶得到的收益相比,往往得不偿失。 \ No newline at end of file diff --git a/C_Universal_Brockchain/markdown/13.md b/C_Universal_Brockchain/markdown/13.md new file mode 100644 index 00000000..f278c4ca --- /dev/null +++ b/C_Universal_Brockchain/markdown/13.md @@ -0,0 +1,111 @@ +# 目前的关键技术和挑战 + +从技术角度讲,区块链所涉及的领域比较繁杂,包括分布式系统、密码学、心理学、经济学、博弈论、控制论、网络协议等,这也意味着我们在工程实践中会面临大量的挑战。 + +下面列出了目前业界关注较多的一些技术话题。 + +# 隐私保护 + +隐私保护一直是分布式系统领域十分关键的问题。在分布式场景下,因为缺乏独立的管理机制,参与网络的各方无法保证严格遵守协议,甚至会故意试图获取网络中他人的数据,对这些行为都很难进行约束。 + +要在共享协同信息和隐私保护之间达到合适的平衡是个不小的挑战,目前,公有账本系统屡屡出现安全漏洞,动辄造成数千万美金损失的风险。随着欧盟《通用数据保护条例》(General Data Protection Regulation,GDPR)的落地,隐私保护的合规要求愈加严格;传统的信息安全技术、形式化验证技术在应对新的需求时暴露出实践性不强的缺陷,都亟待解决。 + +尤其是医疗健康领域,对数据的隐私性需求最为强烈,要求严格控制数据的来源、所有权和使用范围。传统的基于加密的手段很难满足这些要求,需要结合零知识证明、同态加密、隐私查询等新的密码学手段。而这些新技术在实际应用中还存在不少问题。 + +# 分布式共识 + +共识是分布式系统领域经典的技术难题,学术界和业界都已有大量的研究成果(包括 Paxos、拜占庭系列算法等)。 + +问题的核心在于确保某个变更在分布式网络中得到一致的执行结果,是被参与多方都承认的,同时这个信息是不可推翻的。 + +该问题在公开匿名场景下和带权限管理的场景下需求差异较大,从而导致了基于概率的算法和确定性算法两类思想。 + +最初,比特币区块链考虑的是公开匿名场景下的最坏保证。通过引入了“工作量证明”(Proof of Work)策略来规避少数人的恶意行为,并通过概率模型保证最终共识到最长链。算法的核心思想是基于经济利益的博弈,让恶意破坏的参与者损失经济利益,从而保证大部分人的合作。同时,确认必须经过多个区块的生成之后达成,从概率上进行保证。这类算法的主要问题在于效率低下和能源浪费。类似地,还有以权益为抵押的 PoS 和 DPoS 算法等。 + +后来更多的区块链技术(如超级账本)在带权限许可的场景下,开始考虑支持更多的确定性的共识机制,包括改进的拜占庭算法等,可以解决快速确认的问题。但已有算法往往在大规模和动态场景下表现不佳。 + +共识问题在很长一段时间内都将是极具学术价值的研究热点,核心的指标将包括支持规模、容错的节点比例、决策收敛速度、出错后的恢复、动态特性等。PoW 等基于概率的系列算法理论上允许少于一半的不合作节点,PBFT 等确定性算法理论上则允许不超过 1/3 的不合作节点。 + +# 交易性能 + +一般情况下,区块链并不适用于高频交易的场景,但由于金融系统的迫切需求,业界目前积极探讨如何提高其交易性能,包括吞吐量(Throughput)和确认延迟(Latency)两个方面。 + +目前,公开的比特币公有区块链只能支持平均每秒约 7 笔的吞吐量,其安全的交易确认时间为一小时。以太坊公有区块链的吞吐量略高,达到每秒几十笔,但仍不能满足较大的应用。2017 年底,游戏应用 CryptoKitties 就造成了以太坊网络的严重堵塞。 + +这种场景下,为了提高处理性能,一方面可以提升单个节点的性能(如采用高配置的硬件),同时设计优化的策略和并行算法而提高性能;另外一方面可将交易处理卸载(off-load)到链下,只用区块链记录最终交易信息,如比特币社区提出的 [闪电网络](https://lightning.network/lightning-network-paper.pdf) 等设计。类似地,侧链(side chain)、影子链(shadow chain)等思路在当前阶段也有一定的借鉴意义。类似设计可将整体性能提升 1~2 个数量级。 + +**联盟链场景下,参与方在共同的信任前提和利益约束下,可以采取更激进的设计,换取性能的提升。以超级账本 Fabric 项目为例,普通虚拟机配置即可达到每秒数千次的交易吞吐量;在进一步优化或硬件加速情况下可以达到每秒数万次的处理性能。** + +整体来看,目前开源区块链系统已经可以满足大量应用场景的性能需求,但离大规模商用交易系统的吞吐性能(每秒稳定数万笔)还有差距。 + +*注:据公开的数据,VISA 系统的处理均值为 2,000 tps,峰值为 56,000 tps;某大规模金融支付系统的处理峰值超过了 85,000 tps;某大型证券交易所号称的处理均(峰)值在 80,000 tps 左右。* + +# 扩展性 + +常见的分布式系统,可以通过横向增加节点来扩展整个系统的处理能力。 + +对于区块链网络系统来说,跟传统分布式系统不同,这个问题往往并非那么简单。实际上,大部分区块链系统的性能,很大程度上取决于单个节点的处理能力。对这些系统来说,节点需要满足 **高性能、安全、稳定、硬件辅助加解密能力**。 + +例如,对于比特币和以太坊区块链而言,网络中每个参与维护的核心节点都要保持一份完整的存储,并且进行智能合约的处理。此时,整个网络的总存储和计算能力,取决于单个节点的能力。甚至当网络中节点数过多时,可能会因为共识延迟而降低整个网络的性能。尤其在公有网络中,由于大量低性能处理节点的存在,问题将更加明显。 + +要解决这个问题,根本上是放松对每个节点都必须参与完整处理的限制(当然,网络中节点要能合作完成完整的处理),这个思路已经在超级账本等项目中得到应用;同时尽量减少核心层的处理工作,甚至采用多层处理结构来分散交易。 + +在联盟链模式下,还可以专门采用高性能的节点作为核心节点,用相对较弱的节点作为代理访问节点。 + +另外,未来必然会涉及到不同账本之间互通的需求(跨链)。目前无论是基于公证人(Notary)、侧链/中继链锚定(Sidechains / Relays)还是哈希锁定(Hash-locking)机制,在实践中仍存在一些不足。公证人机制往往需要依赖第三方的公证人,存在中心化的弱点;侧链/中继链锚定机制目前应用在资产类转移场景,依赖不同链之间的合约配合;哈希锁定在闪电网络中最早提出,并应用在 W3C 的 Interledger 协议中,目前只支持支付类交换操作,而且要求双方账本理解彼此合约。 + +超级账本的 Quilt 项目和 W3C 的 Interledger Payments 工作组已对此问题开展研究,但离通用的跨链需求还有距离。目前来看,要想解决跨链的扩展性问题,需要有办法打通不同框架,类似路由器来沟通不同的子网。 + +# 安全防护 + +区块链目前最热门的应用场景是金融相关的服务,安全自然是最敏感也是挑战最大的问题。 + +区块链在设计上大量采用了现代成熟的密码学算法和网络通信协议。但这是否就能确保其绝对安全呢? + +**世界上并没有绝对安全的系统。** + +系统越复杂,攻击面越多,安全风险越高;另外系统是由人设计的和运营的,难免出现漏洞。 + +作为分布式系统,区块链首先要考虑传统的网络安全(认证、过滤、攻防)、信息安全(密码配置、密钥管理)、管理安全(审计、风险分析控制)等问题。其次,尤其要注意新场景下凸显的安全挑战。 + +首先是立法。对区块链系统如何进行监管?攻击区块链系统是否属于犯罪?攻击银行系统是要承担后果的。但是目前还没有任何法律保护区块链(特别是公有链)以及基于它的实现。 + +其次是代码实现的漏洞管理。考虑到使用了几十年的 openssl 还带着那么低级的漏洞([heart bleeding](https://heartbleed.com/)),而且是源代码完全开放的情况下,让人不禁对运行中的大量线上系统持谨慎态度。而对于金融系统来说,无论客户端还是平台侧,即便是很小的漏洞都可能造成难以估计的损失。 + +另外,公有区块链所有交易记录都是公开可见的,这意味着所有的交易,即便被匿名化和加密处理,但总会在未来某天被破解。安全界一般认为,只要物理上可接触就不是彻底的安全。实际上,已有文献证明,比特币区块链的交易记录大部分都能追踪到真实用户。 + +公有链普遍缺乏有效的治理和调整机制,一旦运行中出现问题难以及时修正。即使是有人提交了修正补丁,只要有部分既得利益者联合起来反对,就无法得到实施。比特币社区已经出现过多次类似的争论。 + +最后,运行在区块链上的智能合约应用五花八门,可能存在潜在的漏洞,必须要有办法进行安全管控,在注册和运行前进行形式化验证和安全探测,以规避恶意代码的破坏。运行智能合约的环境也会成为攻击的目标。近些年区块链领域的安全事件大都跟智能合约漏洞有关。 + +2014 年 3 月,Mt.gox 交易所宣称其保存的 85 万枚比特币被盗,直接导致破产。 + +2016 年 6 月 17 日,发生 [DAO 系统漏洞被利用](https://blog.daohub.org/the-dao-is-under-attack-8d18ca45011b) 事件,直接导致价值 6000 万美元的数字货币被利用者获取。尽管对于这件事情的反思还在进行中,但事实再次证明,目前基于区块链技术进行生产应用时,务必要细心谨慎地进行设计和验证。必要时,甚至要引入“形式化验证”和人工审核机制。 + +2018 年 3 月,币安交易所被黑客攻击,造成用户持有比特币被大量卖出。虽然事后进行了追回,但仍在短期内对市场价格造成了巨大冲击。 + +*注:著名黑客凯文•米特尼克(Kevin D. Mitnick)所著的《反欺骗的艺术——世界传奇黑客的经历分享》一书中分享了大量的真实社交工程欺骗案例。* + +# 数据库和存储系统 + +区块链网络中的大量信息需要写到文件和数据库中进行存储。 + +观察区块链的应用,大量的读写操作、Hash 计算和验证操作,跟传统数据库的行为十分不同。 + +当年,人们观察到互联网场景中大量非事务性的查询操作,而设计了非关系型(NoSQL)数据库。那么,针对区块链应用的这些特点,是否可以设计出一些特殊的针对性的数据库呢? + +LevelDB、RocksDB 等键值数据库,具备很高的随机写和顺序读、写性能,以及相对较差的随机读的性能,被广泛应用到了区块链信息存储中。但目前来看,面向区块链的数据库技术仍然是需要突破的技术难点之一,特别是如何支持更丰富语义的操作。 + +正如此前预测,目前已经出现了引入区块链特性的“账本数据库”,包括甲骨文和亚马逊等产品。它们通过签名来防抵赖,并追溯数据修改历史,提供审计功能。 + +此外,在高吞吐量的场景下,本地账本结构会快速累积大量数据。这些数据的保存、索引、清理,发生故障后的恢复,新加入节点的数据获取等,都是值得探索的开放问题。 + +# 互操作和运营治理 + +大部分企业内和企业之间都已经存在了一些信息化产品和工具,例如,处于核心位置的数据库、企业信息管理系统、通讯系统等。企业在采用新的产品时,往往会重点考察与已有商业流程和信息系统进行集成时的平滑度。 + +两种系统如何共存,如何分工,彼此的业务交易如何进行合理传递?出现故障如何排查和隔离?已有数据如何在不同系统之间进行迁移和灾备?这些都是很迫切要解决的实际问题。解决不好,将是区块链技术落地的不小阻碍。 + +另外,虽然大部分区块链系统在平台层面都支持了非中心化机制,在运营和治理层面却往往做不到那么非中心化。以比特币网络为例,历史上多次发生过大部分算力集中在少数矿池的情况,同时软件的演化路线集中在少数开发者手中。运营和治理机制是现有区块链系统中普遍缺失的,但在实际应用中又十分重要。 + +如何进行合理的共识、高效的治理仍属于尚未解决的问题。公有账本试图通过将计算机系统中的令牌与经济利益挂钩,维护系统持续运行;联盟账本通过商业合作和投票等方式,推举联盟治理机构,进行联盟网络的维护管理。这些机制仍需在实践过程中不断完善和改进。以供应链场景为例,动辄涉及数百家企业,上下游几十个环节,而且动态性较强。这些都需要分布式账本平台能提供很强的治理投票和权限管控机制。 \ No newline at end of file diff --git a/C_Universal_Brockchain/markdown/14.md b/C_Universal_Brockchain/markdown/14.md new file mode 100644 index 00000000..40a8ae36 --- /dev/null +++ b/C_Universal_Brockchain/markdown/14.md @@ -0,0 +1,19 @@ +# 认识上的误区 + +目前,区块链作为一种相对年轻的技术,自身仍在飞速发展中,在相关概念上仍有一些值得探讨之处。 + +下面总结一些常见的认知误区。 + +**区块链是完全创新的新技术** 作为融合多项已有技术而出现的事物,区块链跟现有记账科技和信息体系是一脉相承的。区块链在解决多方合作和可信计算问题上向前多走了一步,但并不意味着它就是万能的(从来不会存在一项万能的科技),更不会快速颠覆已有的众多商业模式。很长一段时间里,区块链的应用场景仍需不断摸索,区块链在自身发展的同时也会与已有系统共存互通。 + +**区块链必然是非中心化的,非中心化一定优于中心化设计** 比较两种技术的优劣,必须要先确定场景,区块链也是如此。不可能存在某种技术在任意场景下都是最优的。目前区块链的两大形态——公有链和联盟链之所以在技术选型上存在较大差异,正是因为它们面向的场景不同。中心化设计往往具有设计简单、管理完善、性能高、安全可控的特点,但容错性能比较差;**非中心化(多中心化)的设计可以提高容错性能,可以利用多方共识来降低篡改风险,但意味着设计较复杂,性能较差。** + +从实际需求出发,现有大部分区块链技术都介于绝对的中心化和绝对的非中心化之间,以取得不同指标上的平衡。例如某些公有链为了提高性能,选择部分代表节点来参与共识。 + +**区块链离不开加密数字货币** 虽说区块链的早期应用之一是比特币等加密数字货币,但发展到今日,区块链技术早已脱颖而出,两者也各自朝着不同的目标向前发展。比特币侧重从金融角度发掘加密数字货币的实验和实践意义;区块链则从技术层面探讨和研究分布式账本科技的商业价值,**并试图拓展到更多分布式互信的场景。** + +**区块链是一种数据库技术** 虽然区块链中往往使用了已有的数据库技术,也可以用来存储或管理数据(Data Management),但它要面向的主要问题是多方数据互信协作(Data Collaboration)问题,这是传统数据技术无法解决的。单纯从数据存储或管理角度,区块链效率可能不如传统数据库效率高,因此一般不推荐把大量原始数据直接放到区块链系统中。当然,区块链系统可以与现有数据库和大数据系统等进行集成。甲骨文、亚马逊等团队尝试将区块链的一些特性引入到数据库中,提出了 Blockchain Table、Quantum Ledger Database 等新型数据系统,可以满足防篡改历史、方便审计等需求。 + +另一方面,区块链复杂大规模的场景需求也对数据库技术提出了新的需求。例如开源社区普遍使用的 levelDB 在扩展性方面表现并不特别优秀,需要进行增强;部分业务场景下需要支持 SQL 语义。可以借鉴其它数据库(如 RocksDB 和 BerkeleyDB)的一些优势进行改造。 + +**Token 等于加密数字货币** 早在区块链概念出现之前,Token(令牌或凭证)就大量应用在计算机系统中。作为权限证明,它可以协助计算机系统进行认证等操作。作为分布式系统,区块链中很自然也可以在某些场景(如游戏积分)下借用 Token 机制,带来应用生态的管理便利。而加密数字货币试图借用数字化技术来实现货币功能,更强调经济价值,跟计算机系统中的原生功能无必然联系。总之,两者是不同层面的概念,即使不依赖 Token,仍然可以实现加密数字货币;数字凭证只有具备可靠、大范围接受的购买力,才可能成为货币,否则只能作为收藏品在小圈子内流通。 \ No newline at end of file diff --git a/C_Universal_Brockchain/markdown/15.md b/C_Universal_Brockchain/markdown/15.md new file mode 100644 index 00000000..e0521607 --- /dev/null +++ b/C_Universal_Brockchain/markdown/15.md @@ -0,0 +1,86 @@ +# 分布式问题 + +# 一致性问题 + +一致性问题是分布式领域最基础、最重要的问题,也是半个世纪以来的研究热点。 + +随着业务场景越来越复杂,计算规模越来越庞大,单点系统往往难以满足高可扩展(Scalability)和高容错(Fault-tolerance)两方面的需求。此时就需要多台服务器通过组成集群,构建更加强大和稳定的“虚拟超级服务器”。 + +任务量越大,处理集群的规模越大,设计和管理的挑战也就越高。谷歌公司的全球搜索集群系统,包括数十万台服务器,每天响应百亿次的互联网搜索请求。 + +集群系统要实现一致性不是一件容易的事。不同节点可能处于不同的状态,不同时刻收到不同的请求,而且随时可能有节点出现故障。要保持对外响应的“一致性”,好比训练一群鸭子齐步走,难度可想而知。 + +# 定义与重要性 + +**定义** 一致性(Consistency),早期也叫(Agreement),在分布式系统领域中是指对于多个服务节点,给定一系列操作,在约定协议的保障下,使得它们对处理结果达成“某种程度”的协同。 + +理想情况(不考虑节点故障)下,如果各个服务节点严格遵循相同的处理协议(即构成相同的状态机逻辑),则在给定相同的初始状态和输入序列时,可以确保处理过程中的每个步骤的执行结果都相同。因此,传统分布式系统中讨论一致性,往往是指在外部任意发起请求(如向多个节点发送不同请求)的情况下,确保系统内大部分节点实际处理请求序列的一致,即对请求进行全局排序。 + +那么,为什么说一致性问题十分重要呢? + +举个现实生活中的例子,多个售票处同时出售某线路上的火车票,该线路上存在多个经停站,怎么才能保证在任意区间都不会出现超售(同一个座位卖给两个人)的情况? + +这个问题看起来似乎没那么难,现实生活中经常通过分段分站售票的机制。然而,要支持海量的用户进行并行购票,并非易事(如 12306 网站高峰期日均访问量超过 500 亿次)。特别是计算机系统往往需要达到远超物理世界的高性能和高可扩展性需求,挑战会变得更大。这也是为何每年到了促销季,各大电商平台都要提前完善系统。 + +*注:一致性关注的是系统呈现的状态,并不关注结果是否正确;例如,所有节点都对某请求达成否定状态也是一种一致性。* + +# 问题挑战 + +计算机固然很快,但在很多地方都比人类世界脆弱的多。典型的,在分布式系统中,存在不少挑战: + +- 节点完成请求的时间无法保障,处理的结果可能是错误的,甚至节点自身随时可能发生故障; +- 为了实现可扩展性,集群往往要采用异步设计,依靠网络消息交互,意味着不可预测的通信延迟、丢失或错误。 + +仍以火车票售卖问题为例,愿意动脑筋的读者可能已经想到了一些不错的解决思路,例如: + +- 要出售任意一张票前,先打电话给其他售票处,确认下当前这张票不冲突。即退化为同步调用来避免冲突; +- 多个售票处提前约好隔离的售票时间。比如第一家可以在上午 8 点到 9 点期间卖票,接下来一个小时是另外一家……即通过令牌机制来避免冲突; +- 成立一个第三方的存票机构,票集中存放,每次卖票前找存票机构查询。此时问题退化为中心化中介机制。 + +这些思路假设售票处都能保证正常工作,并且消息传递不会出现错误。 + +当然,还可以设计出更多更完善的方案,但它们背后的核心思想,都是**将可能引发不一致的并行操作进行串行化**。这实际上也是现代分布式系统处理一致性问题的基础思路。另外,由于普通计算机硬件和软件的可靠性不足,在工程实现上还要对核心部件稳定性进行加强。 + +反过来,如果节点都很鲁棒,性能足够强,同时网络带宽足够大、延迟足够低,这样的集群系统往往更容易实现一致性。 + +然而,真实情况可能比人们预期的糟糕。2015年,论文《Taming Uncertainty in Distributed Systems with Help from the Network》中指出,即便部署了专业设备和冗余网络的中等规模的数据中心,每个月发生的网络故障高达 12 次。 + +# 一致性的要求 + +规范来看,分布式系统达成一致的过程,应该满足: + +- 可终止性(Termination):一致的结果在有限时间内能完成; +- 约同性(Agreement):不同节点最终完成决策的结果是相同的; +- ····合法性(Validity):决策的结果必须是某个节点提出的提案。 + +可终止性很容易理解。有限时间内完成,意味着可以保障提供服务(Liveness)。这是计算机系统可以被正常使用的前提。需要注意,在现实生活中这点并不是总能得到保障的。例如取款机有时候会出现“服务中断”;拨打电话有时候是“无法连接”的。 + +约同性看似容易,实际上暗含了一些潜在信息。决策的结果相同,意味着算法要么不给出结果,任何给出的结果必定是达成了共识的,即安全性(Safety)。挑战在于算法必须要考虑的是可能会处理任意的情形。凡事一旦推广到任意情形,往往就不像看起来那么简单。例如现在就剩一张某区间(如北京 --> 南京)的车票了,两个售票处也分别刚通过某种方式确认过这张票的存在。这时,两家售票处几乎同时分别来了一个乘客要买这张票,从各自“观察”看来,自己一方的乘客都是先到的……这种情况下,怎么能达成对结果的共识呢?看起来很容易,卖给物理时间上率先提交请求的乘客即可。然而,对于两个来自不同位置的请求来说,要判断在时间上的“先后”关系并不是那么容易。两个车站的时钟时刻可能是不一致的;时钟计时可能不精确的……根据相对论的观点,不同空间位置的时间是不一致的。因此追求绝对时间戳的方案是不可行的,能做的是要对事件的发生进行排序。 + +事件发生的相对先后顺序(逻辑时钟)十分重要,确定了顺序,就没有了分歧。这也是解决分布式系统领域很多问题的核心秘诀:**把不同时空发生的多个事件进行全局唯一排序,而且这个顺序还得是大家都认可的**。 + +如果存在可靠的物理时钟,实现排序往往更为简单。高精度的石英钟的漂移率为 10^{-7}10−7,最准确的原子震荡时钟的漂移率为 10^{-13}10−13。Google 曾在其分布式数据库 Spanner 中采用基于原子时钟和 GPS 的“TrueTime”方案,能够将不同数据中心的时间偏差控制在 10ms 置信区间。在不考虑成本的前提下,这种方案简单、有效。然而,计算机系统的时钟误差要大得多,这就造成分布式系统达成一致顺序十分具有挑战。 + +*注:Leslie Lamport 在 1978 年发表的论文《Time, Clocks and the Ordering of Events in a Distributed System》中将分布式系统中顺序与相对论进行对比,提出了偏序关系的观点。而根据相对论,并不存在绝对的时间。因此,先后顺序可能更有意义。* + +最后的合法性看似绕口,但其实比较容易理解,即达成的结果必须是节点执行操作的结果。仍以卖票为例,如果两个售票处分别决策某张票出售给张三和李四,那么最终达成一致的结果要么是张三,要么是李四,而不能是其他人。 + +# 带约束的一致性 + +从前面的分析可以看出,要实现理想的严格一致性(Strict Consistency)代价很大。除非系统所有节点都不发生任何故障,而且节点间通信没有延迟,此时整个系统等价于一台机器。实际上,实现较强的一致性要求同步操作,容错性差,同时会牺牲部分性能和可扩展性。实际系统往往会选择不同强度的一致性,主要包括强一致性(Strong Consistency)和弱一致性(Weak Consistency)两大类。 + +强一致性主要包括顺序一致性([Sequential Consistency](https://en.wikipedia.org/wiki/Sequential_consistency))和线性一致性([Linearizability Consistency](https://en.wikipedia.org/wiki/Linearizability): + + + ++ 顺序一致性: + + 又叫因果一致性,最早由 Leslie Lamport 1979 年经典论文《How to Make a Multiprocessor Computer That Correctly Executes Multiprocess Programs》中提出,是一种较强的约束。所有进程看到的全局执行顺序(total order)一致(否则数据副本就不一致了);并且每个进程看自身操作的顺序(local order)跟实际发生顺序一致。例如,某进程先执行 A,后执行 B,则实际得到的全局结果(其它进程也看到这个结果)中就应该为 A 在 B 前面,而不能反过来。如果另一个进程先后执行了C、D操作,则全局顺序可以共识为 A、B、C、D 或 A、C、B、D 或 C、A、B、D 或 C、D、A、B 的一种(即 A、B 和 C、D 的组合),决不能出现 B、A 或 D、C。顺序一致性实际上限制了各进程自身指令的偏序关系,但不需要在进程间按照物理时间进行全局排序,属于实践中应用较多的强一致性。以算盘为例,每个进程的事件是某根横轴上的算珠,它们可以前后拨动(改变不同进程之间先后顺序),但同一个横轴上的算珠的相对先后顺序无法改变。 + + + ++ 线性一致性: + + 由 Maurice P. Herlihy 与 Jeannette M. Wing 在 1990 年经典论文《Linearizability: A Correctness Condition for Concurrent Objects》中共同提出,是最强的一致性。它在顺序一致性前提下增加了进程间的操作顺序要求,形成理想化完全一致的全局顺序。线性一致性要求系统看起来似乎只有一个数据副本,客户端操作都是原子的,并且顺序执行;所有进程的操作似乎是实时同步的,并且跟实际发生顺序一致。例如某个客户端写入成功,则其它客户端将立刻看到最新的值。线性一致性下所有进程的所有事件似乎都处于同一个横轴,存在唯一的先后顺序。线性一致性较难实现,目前基本上要么依赖于全局的时钟或锁,要么通过一些共识算法,性能往往不高。 + +强一致性比较难实现,很多场景下可以放宽对一致性的要求,采用部分异步操作,从而提升性能、可扩展性,降低响应延迟,这些在某些方面弱化的一致性都笼统称为弱一致性。例如互联网领域常用的最终一致性(Eventual Consistency),允许出现临时不一致或看到过时数据,但在经历一段时间后可以达到一致的状态。例如电商购物时将某物品放入购物车,但是可能在最终付款时才提示物品已经售罄了。 \ No newline at end of file diff --git a/C_Universal_Brockchain/markdown/16.md b/C_Universal_Brockchain/markdown/16.md new file mode 100644 index 00000000..23d2d361 --- /dev/null +++ b/C_Universal_Brockchain/markdown/16.md @@ -0,0 +1,61 @@ +# 共识算法 + +共识(Consensus)这个术语很多时候会与一致性(Consistency)术语放在一起讨论。严谨地讲,两者的含义并不完全相同。 + +一致性的含义比共识宽泛,在不同场景(基于事务的数据库、分布式系统等)下意义不同。具体到分布式系统场景下,一致性指的是多个副本对外呈现的状态。如前面提到的顺序一致性、线性一致性,描述了多节点对数据状态的共同维护能力。而共识,则特指在分布式系统中多个节点之间对某个事情(例如多个事务请求,先执行谁?)达成一致看法的过程。因此,达成某种共识并不意味着就保障了一致性。 + +实践中,要保障系统满足不同程度的一致性,往往需要通过共识算法来达成。 + +共识算法解决的是分布式系统对某个提案(Proposal),大部分节点达成一致意见的过程。提案的含义在分布式系统中十分宽泛,如多个事件发生的顺序、某个键对应的值、谁是主节点……等等。可以认为任何可以达成一致的信息都是一个提案。 + +对于分布式系统来讲,各个节点通常都是相同的确定性状态机模型(又称为状态机复制问题,State-Machine Replication),从相同初始状态开始接收相同顺序的指令,则可以保证相同的结果状态。因此,系统中多个节点最关键的是对多个事件的顺序进行共识,即排序。 + +*注:计算机世界里采用的是典型的“多数人暴政”,足够简单、高效。* + +# 问题与挑战 + +无论是在现实生活中,还是计算机世界里,达成共识都要解决两个基本的问题: + +- + + 首先,如何提出一个待共识的提案?如通过令牌传递、随机选取、权重比较、求解难题等; + +- + + 其次,如何让多个节点对该提案达成共识(同意或拒绝),如投票、规则验证等。 + +理论上,如果分布式系统中各节点都能以十分“理想”的性能(瞬间响应、超高吞吐)稳定运行,节点之间通信瞬时送达(如量子纠缠),则实现共识过程并不十分困难,简单地通过广播进行瞬时投票和应答即可。 + +可惜的是,现实中这样的“理想”系统并不存在。不同节点之间通信存在延迟(光速物理限制、通信处理延迟),并且任意环节都可能存在故障(系统规模越大,发生故障可能性越高)。如通信网络会发生中断、节点会发生故障、甚至存在被入侵的节点故意伪造消息,破坏正常的共识过程。 + +一般地,把出现故障(Crash 或 Fail-stop,即不响应)但不会伪造信息的情况称为“非拜占庭错误(Non-Byzantine Fault)”或“故障错误(Crash Fault)”;伪造信息恶意响应的情况称为“拜占庭错误”(Byzantine Fault),对应节点为拜占庭节点。显然,后者场景中因为存在“捣乱者”更难达成共识。 + +此外,任何处理都需要成本,共识也是如此。当存在一定信任前提(如接入节点都经过验证、节点性能稳定、安全保障很高)时,达成共识相对容易,共识性能也较高;反之在不可信的场景下,达成共识很难,需要付出较大成本(如时间、经济、安全等),而且性能往往较差(如工作量证明算法)。 + +*注:非拜占庭场景的典型例子是通过报数来统计人数,即便偶有冲突(如两人同时报一个数)也能很快解决;拜占庭场景的一个常见例子是“杀人游戏”,当参与者众多时很难快速达成共识。* + +# 常见算法 + +根据解决的场景是否允许拜占庭错误情况,共识算法可以分为 Crash Fault Tolerance (CFT) 和 Byzantine Fault Tolerance(BFT)两类。 + +对于非拜占庭错误的情况,已经存在不少经典的算法,包括 Paxos(1990 年)、Raft(2014 年)及其变种等。这类容错算法往往性能比较好,处理较快,容忍不超过一半的故障节点。 + +对于要能容忍拜占庭错误的情况,包括 PBFT(Practical Byzantine Fault Tolerance,1999 年)为代表的确定性系列算法、PoW(1997 年)为代表的概率算法等。确定性算法一旦达成共识就不可逆转,即共识是最终结果;而概率类算法的共识结果则是临时的,随着时间推移或某种强化,共识结果被推翻的概率越来越小,最终成为事实上结果。拜占庭类容错算法往往性能较差,容忍不超过 1/3 的故障节点。 + +此外,XFT(Cross Fault Tolerance,2015 年)等最近提出的改进算法可以提供类似 CFT 的处理响应速度,并能在大多数节点正常工作时提供 BFT 保障。 + +Algorand 算法(2017 年)基于 PBFT 进行改进,通过引入可验证随机函数解决了提案选择的问题,理论上可以在容忍拜占庭错误的前提下实现更好的性能(1000+ TPS)。 + +*注:实践中,对客户端来说要拿到共识结果需要自行验证,典型地,可访问足够多个服务节点来比对结果,确保获取结果的准确性。* + +# 理论界限 + +科学家都喜欢探寻问题最坏情况的理论界限。那么,共识问题的最坏界限在哪里呢?很不幸,在推广到任意情况时,分布式系统的共识问题无通用解。 + +这似乎很容易理解,当多个节点之间的通信网络自身不可靠情况下,很显然,无法确保实现共识(例如,所有涉及共识的消息都丢失)。那么,对于一个设计得当,可以大概率保证消息正确送达的网络,是不是一定能获得共识呢? + +理论证明告诉我们,**即便在网络通信可靠情况下,一个可扩展的分布式系统的共识问题通用解法的下限是——没有下限(无解)。** + +这个结论,被称为“FLP 不可能原理”。该原理极其重要,可以看做是分布式领域里的“测不准原理”。 + +*注:不光分布式系统领域,实际上很多领域都存在类似测不准原理的约束,或许说明世界本源就存在限制。* \ No newline at end of file diff --git a/C_Universal_Brockchain/markdown/17.md b/C_Universal_Brockchain/markdown/17.md new file mode 100644 index 00000000..94b645e6 --- /dev/null +++ b/C_Universal_Brockchain/markdown/17.md @@ -0,0 +1,40 @@ +FLP 不可能原理 + + + +# 定义 + +**FLP 不可能原理**:在网络可靠、但允许节点失效(即便只有一个)的最小化异步模型系统中,不存在一个可以解决一致性问题的确定性共识算法(No completely asynchronous consensus protocol can tolerate even a single unannounced process death)。 + +提出并证明该定理的论文《Impossibility of Distributed Consensus with One Faulty Process》是由 Fischer、Lynch 和 Patterson 三位科学家于 1985 年发表,该论文后来获得了 Dijkstra(就是发明最短路径算法的那位计算机科学家)奖。 + +FLP 不可能原理告诉我们,**不要浪费时间去试图为异步分布式系统设计面向任意场景的共识算法**。 + +# 如何理解 + +要正确理解 FLP 不可能原理,首先要弄清楚“异步”的含义。 + +在分布式系统中,同步和异步这两个术语存在特殊的含义: + +- 同步,是指系统中的各个节点的时钟误差存在上限;并且消息传递必须在一定时间内完成,否则认为失败;同时各个节点完成处理消息的时间是一定的。因此同步系统可以轻易判断是节点宕机还是传输消息丢失; +- 异步,则意味着系统中各个节点可能存在较大的时钟差异;同时消息传输时间是任意长的;各节点对消息进行处理的时间也可能是任意长的。这就造成无法判断某个消息迟迟没有被响应是哪里出了问题(节点故障还是传输故障?)。不幸地是,现实生活中的系统往往都是异步系统。 + +FLP 不可能原理在论文中以图论的形式进行了严格证明。要理解其基本原理并不复杂,一个不严谨的例子如下。 + +三个人在不同房间进行投票(投票结果是 0 或者 1),彼此可以通过电话进行沟通,但经常有人会时不时睡着。比如某个时候,A 投票 0,B 投票 1,C 收到了两人的投票,然后 C 睡着了。此时,A 和 B 将永远无法在有限时间内获知最终的结果,因为他们无法判断究竟是 C 没有应答还是应答的时间过长。如果可以重新投票,则类似情形可以在每次取得结果前发生,这将导致共识过程永远无法完成。 + +FLP 不可能原理实际上说明对于允许节点失效情况下,纯粹异步系统无法确保共识在有限时间内完成。即便对于非拜占庭错误的前提下,包括 Paxos、Raft 等算法也都存在无法达成共识的极端情况,只是在工程实践中这种情况出现的概率很小。 + +那么,这是否意味着研究共识算法压根没有意义? + +不必如此悲观。学术研究,往往考虑地是数学和物理意义上理想化的情形,很多时候现实世界要稳定得多(感谢这个世界如此鲁棒!)。例如,上面例子中描述的最坏情形,每次都发生的概率其实并没有那么大。工程实现上,若某次共识失败,再尝试几次,很大可能就成功了。 + +**科学告诉你,什么是不可能的;工程则告诉你,付出一些代价,可以把它变成可行。** + +这就是科学和工程不同的魅力。FLP 不可能原理告诉大家不必浪费时间去追求完美的共识方案,而要根据实际情况设计可行的工程方案。 + +那么,退一步讲,在付出一些代价的情况下,共识能做到多好? + +回答这一问题的是另一个很出名的原理:CAP 原理。 + +*注:科学告诉你去赌场是愚蠢的,因为最终总会输钱;工程则告诉你,如果你愿意接受最终输钱的风险,中间说不定能偶尔小赢几笔呢!* \ No newline at end of file diff --git a/C_Universal_Brockchain/markdown/18.md b/C_Universal_Brockchain/markdown/18.md new file mode 100644 index 00000000..ca10245c --- /dev/null +++ b/C_Universal_Brockchain/markdown/18.md @@ -0,0 +1,49 @@ +# CAP 原理 + + + +CAP 原理最早出现在 2000 年,由加州大学伯克利分校的 Eric Brewer 教授在 ACM 组织的 Principles of Distributed Computing(PODC)研讨会上提出的猜想。两年后,麻省理工学院的 Nancy Lynch 等学者进行了理论证明。 + +该原理被认为是分布式系统领域的重要原理之一,深刻影响了分布式计算与系统设计的发展。 + +# 定义 + +**CAP 原理**:分布式系统无法同时确保一致性(Consistency)、可用性(Availability)和分区容忍性(Partition),设计中往往需要弱化对某个特性的需求。 + +一致性、可用性和分区容忍性的具体含义如下: + +- 一致性(Consistency):任何事务应该都是原子的,所有副本上的状态都是事务成功提交后的结果,并保持强一致; +- 可用性(Availability):系统(非失败节点)能在有限时间内完成对操作请求的应答; +- 分区容忍性(Partition):系统中的网络可能发生分区故障(成为多个子网,甚至出现节点上线和下线),即节点之间的通信无法保障。而网络故障不应该影响到系统正常服务。 + +CAP 原理认为,分布式系统最多只能保证三项特性中的两项特性。 + +比较直观地理解,当网络可能出现分区时候,系统是无法同时保证一致性和可用性的。要么,节点收到请求后因为没有得到其它节点的确认而不应答(牺牲可用性),要么节点只能应答非一致的结果(牺牲一致性)。 + +由于大部分时候网络被认为是可靠的,因此系统可以提供一致可靠的服务;当网络不可靠时,系统要么牺牲掉一致性(多数场景下),要么牺牲掉可用性。 + +*注意:网络分区是可能存在的,出现分区情况后很可能会导致发生“脑裂”现象。* + +# 应用场景 + +既然 CAP 三种特性不可同时得到保障,则设计系统时候必然要弱化对某个特性的支持。 + +## 弱化一致性 + +对结果一致性不敏感的应用,可以允许在新版本上线后过一段时间才最终更新成功,期间不保证一致性。 + +例如网站静态页面内容、实时性较弱的查询类数据库等,简单分布式同步协议如 Gossip,以及 CouchDB、Cassandra 数据库等,都为此设计。 + +## 弱化可用性 + +对结果一致性很敏感的应用,例如银行取款机,当系统故障时候会拒绝服务。MongoDB、Redis、MapReduce 等为此设计。 + +Paxos、Raft 等共识算法,主要处理这种情况。在 Paxos 类算法中,可能存在着无法提供可用结果的情形,同时允许少数节点离线。 + +## 弱化分区容忍性 + +现实中,网络分区出现概率较小,但很难完全避免。 + +两阶段的提交算法,某些关系型数据库以及 ZooKeeper 主要考虑了这种设计。 + +实践中,网络可以通过双通道等机制增强可靠性,实现高稳定的网络通信。 \ No newline at end of file diff --git a/C_Universal_Brockchain/markdown/19.md b/C_Universal_Brockchain/markdown/19.md new file mode 100644 index 00000000..256fb14e --- /dev/null +++ b/C_Universal_Brockchain/markdown/19.md @@ -0,0 +1,79 @@ +# ACID 原则与多阶段提交 + +# ACID 原则 + +ACID,即 Atomicity(原子性)、Consistency(一致性)、Isolation(隔离性)、Durability(持久性)四种特性的缩写。 + +ACID 也是一种比较出名的描述一致性的原则,通常出现在分布式数据库等基于事务过程的系统中。 + +具体来说,ACID 原则描述了分布式数据库需要满足的一致性需求,同时允许付出可用性的代价。 + +- + + Atomicity:每次事务是原子的,事务包含的所有操作要么全部成功,要么全部不执行。一旦有操作失败,则需要回退状态到执行事务之前; + +- + + Consistency:数据库的状态在事务执行前后的状态是一致的和完整的,无中间状态。即只能处于成功事务提交后的状态; + +- + + Isolation:各种事务可以并发执行,但彼此之间互相不影响。按照标准 SQL 规范,从弱到强可以分为未授权读取、授权读取、可重复读取和串行化四种隔离等级; + +- + + Durability:状态的改变是持久的,不会失效。一旦某个事务提交,则它造成的状态变更就是永久性的。 + +与 ACID 相对的一个原则是 eBay 技术专家 Dan Pritchett 提出的 BASE(Basic Availability,Soft-state,Eventual Consistency)原则。BASE 原则面向大型高可用分布式系统,主张牺牲掉对强一致性的追求,而实现最终一致性,来换取一定的可用性。 + +*注:ACID 和 BASE 在英文中分别是“酸”和“碱”,看似对立,实则是对 CAP 三特性的不同取舍。* + +# 两阶段提交 + +对于分布式事务一致性的研究成果包括著名的两阶段提交算法(Two-phase Commit,2PC)和三阶段提交算法(Three-phase Commit,3PC)。 + +两阶段提交算法最早由 Jim Gray 于 1979 年在论文《Notes on Database Operating Systems》中提出。其基本思想十分简单,既然在分布式场景下,直接提交事务可能出现各种故障和冲突,那么可将其分解为预提交和正式提交两个阶段,规避风险。 + +- + + 预提交(PreCommit):协调者(Coordinator)发起执行某个事务的执行并等待结果。各参与者(Participant)执行事务但不提交,反馈是否能完成,并阻塞等待协调者指令; + +- + + 正式提交(DoCommit):协调者如果得到所有参与者的成功答复,则发出正式提交指令,否则发出状态回滚指令。 + +两阶段提交算法因为其简单容易实现的优点,在关系型数据库等系统中被广泛应用。当然,其缺点也很明显。 + +- + + 第一阶段时,各参与者同步阻塞等待时无法处理请求,会导致系统性较差; + +- + + 存在协调者单点故障问题,最坏情况下协调者总是在第二阶段故障,无法完成提交; + +- + + 可能产生数据不一致的情况。例如第二个阶段时,协调者将正式提交请求发给部分参与者后发生故障。 + +# 三阶段提交 + +三阶段提交算法针对两阶段提交算法第一阶段中参与者阻塞问题进行了优化。具体来说,将预提交阶段进一步拆成两个步骤:询问提交和预提交。 + +完整过程如下: + +- + + 询问提交(CanCommit):协调者询问参与者是否能进行某个事务的提交。参与者需要返回答复是否准备好,但无需执行提交,也无需阻塞。这就避免出现参与者被阻塞的情况; + +- + + 预提交(PreCommit):协调者检查收集到的答复,如果全部为真,则发起执行事务请求。各参与参与者(Participant)需要执行事务但不提交,并反馈能否完成。注意此时说明所有参与者都已经处于准备好状态。; + +- + + 正式提交(DoCommit):协调者如果得到所有参与者的成功答复,则发出正式提交请求,否则发出状态回滚指令。本阶段时,如果参与者一直收不到请求,则超时后继续提交。 + +三阶段提交主要解决了阻塞问题和协调者单点故障问题。第三阶段时,如果参与者无法及时收到协调者的消息,可以在超时后自动进行提交。但是当协调者发出的回滚消息未被部分参与者收到时,会出现不一致的情况。 + +其实,无论两阶段还是三阶段提交,都只是一定程度上缓解了提交冲突的问题,并无法确保系统的一致性。首个有效的共识算法是后来提出的 Paxos 算法。 \ No newline at end of file diff --git a/C_Universal_Brockchain/2.md b/C_Universal_Brockchain/markdown/2.md similarity index 100% rename from C_Universal_Brockchain/2.md rename to C_Universal_Brockchain/markdown/2.md diff --git a/C_Universal_Brockchain/markdown/20.md b/C_Universal_Brockchain/markdown/20.md new file mode 100644 index 00000000..6559f81b --- /dev/null +++ b/C_Universal_Brockchain/markdown/20.md @@ -0,0 +1,141 @@ +# Paxos 算法与 Raft 算法 + + + +Paxos 问题是指分布式的系统中存在故障(crash fault),但不存在恶意(corrupt)节点的场景(即可能消息丢失或重复,但无错误消息)下如何达成共识。这也是分布式共识领域最为常见的问题。因为最早是 Leslie Lamport 用 Paxos 岛的故事对该算法进行描述的,因而得名。解决 Paxos 问题的算法主要有 Paxos 系列算法和 Raft 算法。 + +# Paxos 算法 + +1988 年,Brian M. Oki 和 Barbara H. Liskov 在论文《Viewstamped Replication: A New Primary Copy Method to Support Highly-Available Distributed Systems》中首次提出了解决 Paxos 问题的算法。 + +1990 年由 Leslie Lamport 在论文《The Part-time Parliament》中提出的 [Paxos](http://research.microsoft.com/users/lamport/pubs/lamport-paxos.pdf) 共识算法,在工程角度实现了一种最大化保障分布式系统一致性(存在极小的概率无法实现一致)的机制。Paxos 算法本质上与前者相同,被广泛应用在 Chubby、ZooKeeper 这样的分布式系统中。Leslie Lamport 作为分布式系统领域的早期研究者,因为相关杰出贡献获得了 2013 年度图灵奖。 + +论文中为了描述问题编造了一个虚构故事:在古代爱琴海的 Paxos 岛,议会如何通过表决来达成共识。议员们通过信使传递消息来对议案进行表决。但议员可能离开,信使可能走丢,甚至重复传递消息。 + +Paxos 是首个得到证明并被广泛应用的共识算法,其原理类似于 [两阶段提交](https://en.wikipedia.org/wiki/Two-phase_commit_protocol) 算法,进行了泛化和扩展,通过消息传递来逐步消除系统中的不确定状态。 + +作为后来很多共识算法(如 Raft、ZAB 等)的基础,Paxos 算法基本思想并不复杂,但最初论文中描述比较难懂,甚至连发表也几经波折。2001 年,Leslie Lamport 还专门发表论文《Paxos Made Simple》进行重新解释。 + +*注:Leslie Lamport 对密码学也有研究,1979 年提出的多 Hash 签名机制,具备抗量子计算攻击特性。* + +## 基本原理 + +算法中存在三种逻辑角色的节点,在实现中同一节点可以担任多个角色: + +- + + 提案者(Proposer):提出一个提案,等待大家批准(Chosen)为决议(Value)。系统中提案都拥有一个自增的唯一提案号。往往由客户端担任该角色; + +- + + 接受者(Acceptor):负责对提案进行投票,接受(Accept)提案。往往由服务端担任该角色; + +- + + 学习者(Learner):获取批准结果,并帮忙传播,不参与投票过程。可为客户端或服务端。 + +算法需要满足安全性(Safety) 和存活性(Liveness)两方面的约束要求。实际上这两个基础属性也是大部分分布式算法都该考虑的: + +- + + Safety:保证决议(Value)结果是对的,无歧义的,不会出现错误情况。 * 只有是被提案者提出的提案才可能被最终批准; + + - + + 在一次执行中,只批准(chosen)一个最终决议。被多数接受(accept)的结果成为决议。 + +- + + Liveness:保证决议过程能在有限时间内完成。 + + - + + 决议总会产生,并且学习者能获得被批准的决议。 + +基本思路类似两阶段提交:多个提案者先要争取到提案的权利(得到大多数接受者的支持);成功的提案者发送提案给所有人进行确认,得到大部分人确认的提案成为批准的决议。 + +Paxos 并不保证系统总处在一致的状态。但由于每次达成共识至少有超过一半的节点参与,这样最终整个系统都会获知共识结果。一个潜在的问题是提案者在提案过程中出现故障,这可以通过超时机制来缓解。极为凑巧的情况下,每次新一轮提案的提案者都恰好故障,又或者两个提案者恰好依次提出更新的提案,则导致活锁,系统会永远无法达成共识(实际发生概率很小)。 + +Paxos 能保证在超过一半的节点正常工作时,系统总能以较大概率达成共识。读者可以试着自己设计一套非拜占庭容错下基于消息传递的异步共识方案,会发现在满足各种约束情况下,算法过程总会十分类似 Paxos 的过程。这也是为何 Google Chubby 的作者 Mike Burrows 说:“这个世界上只有一种一致性算法,那就是 Paxos(There is only one consensus protocol, and that's Paxos)”。 + +下面,由简单情况逐步推广到一般情况来探讨算法过程。 + +## 单个提案者+多接受者 + +如果系统中限定只允许某个特定节点是提案者,那么共识结果很容易能达成(只有一个方案,要么达成,要么失败)。提案者只要收到了来自多数接受者的投票,即可认为通过,因为系统中不存在其他的提案。 + +但此时一旦提案者故障,则整个系统无法工作。 + +## 多个提案者+单个接受者 + +限定某个特定节点作为接受者。这种情况下,共识也很容易达成,接受者收到多个提案,选第一个提案作为决议,发送给其它提案者即可。 + +缺陷也是容易发生单点故障,包括接受者故障或首个提案者节点故障。 + +以上两种情形其实类似主从模式,虽然不那么可靠,但因为原理简单而被广泛采用。 + +当提案者和接受者都推广到多个的情形,会出现一些挑战。 + +## 多个提案者+多个接受者 + +既然限定单提案者或单接受者都会出现故障,那么就得允许出现多个提案者和多个接受者。问题一下子变得复杂了。 + +一种情况是同一时间片段(如一个提案周期)内只有一个提案者,这时可以退化到单提案者的情形。需要设计一种机制来保障提案者的正确产生,例如按照时间、序列、或者大家猜拳(出一个参数来比较)之类。考虑到分布式系统要处理的工作量很大,这个过程要尽量高效,满足这一条件的机制非常难设计。 + +另一种情况是允许同一时间片段内可以出现多个提案者。那同一个节点可能收到多份提案,怎么对它们进行区分呢?如果一个节点只接受它收到的首个提案,将导致不同节点可能接受不同的提案。很自然地,提案需要带上不同的序号。节点根据序号来判断接受哪个提案。通常采用递增序号,选择接受序号最大的提案。这是因为旧提案可能基于过期数据,导致失败概率更大。 + +如何为提案分配序号呢?一种可能方案是每个节点的提案数字区间彼此隔离开,互相不冲突。为了满足递增的需求可以配合用时间戳作为前缀字段。 + +同时允许多个提案,意味着很可能单个提案人无法集齐足够多的投票;另一方面,提案者即便收到了多数接受者的投票,也不敢说就一定通过。因为在此过程中投票者无法获知其它投票人的结果,也无法确认提案人是否收到了自己的投票。因此,需要实现两个阶段的提交过程。 + +## 两阶段的提交 + +提案者发出提案申请之后,会收到来自接受者的反馈。一种结果是提案被大多数接受者接受了,一种结果是没被接受。没被接受的话,可以过会再重试。即便收到来自大多数接受者的答复,也不能认为就最终确认了。因为这些接受者自己并不知道自己刚答复的提案可以构成大多数的一致意见。 + +很自然的,需要引入新的一个阶段,即提案者在第一阶段拿到所有的反馈后,需要再次判断这个提案是否得到大多数的支持,如果支持则需要对其进行最终确认。 + +Paxos 里面对这两个阶段分别命名为准备(Prepare)和提交(Commit)。准备阶段通过锁来解决对哪个提案内容进行确认的问题,提交阶段解决大多数确认最终值的问题。 + +**准备阶段**: + +- + + 提案者向多个接受者发送计划提交的提案编号 n,试探是否可以锁定多数接受者的支持; + +- + + 接受者 i 收到提案编号 n,检查回复过的提案的最大编号 M_i。如果 n > M_i,则向提案者返回准备接受(accept)提交的最大编号的提案 P_i(如果还未接受过任何提案,则为空),并不再接受小于 n 的提案,同时更新 M_i = n。这一步是让接受者筛选出它收到的最大编号的提案,接下来只接受其后续提交。 + +**提交阶段**: + +- + + 某个提案者如果收到大多数接受者的回复(表示大部分人收到了 n),则准备发出带有 n 的提交消息。如果收到的回复中带有提案 P_i(说明自己看到的信息过期),则替换选编号最大的 P_i 的值为提案值;否则指定一个新提案值。如果没收到大多数回复,则再次发出请求; + +- + + 接受者 i 收到序号为 n 的提交消息,如果发现 n >= P_i 的序号,则接受提案,并更新 P_i 序号为 n。 + +一旦多数接受者接受了共同的提案值,则形成决议,成为最终确认。之后可以开始新一轮的提交确认。 + +需要注意,Paxos 并不一定能保证每一轮都能提交提案。 + +# Raft 算法 + +Paxos 算法虽然给出了共识设计,但并没有讨论太多实现细节,也并不重视工程上的优化,因此后来在学术界和工程界出现了一些改进工作,包括 Fast Paxos、Multi-Paxos,Zookeeper Atomic Broadcast(ZAB)和 Raft 等。这些算法重点在于改进执行效率和可实现性。 + +其中,[Raft](https://ramcloud.atlassian.net/wiki/download/attachments/6586375/raft.pdf) 算法由斯坦福大学的 Diego Ongaro 和 John Ousterhout 于 2014 年在论文《In Search of an Understandable Consensus Algorithm》中提出,基于 Multi-Paxos 算法进行重新简化设计和实现,提高了工程实践性。Raft 算法的主要设计思想与 ZAB 类似,通过先选出领导节点来简化流程和提高效率。实现上解耦了领导者选举、日志复制和安全方面的需求,并通过约束减少了不确定性的状态空间。 + +算法包括三种角色:领导者(Leader)、候选者(Candidate) 和跟随者(Follower),每个任期内选举一个全局的领导者。领导者角色十分关键,决定日志(log)的提交。每个日志都会路由到领导者,并且只能由领导者向跟随者单向复制。 + +典型的过程包括两个主要阶段: + +- + + 领导者选举:开始所有节点都是跟随者,在随机超时发生后未收到来自领导者或候选者消息,则转变角色为候选者(中间状态),提出选举请求。最近选举阶段(Term)中得票超过一半者被选为领导者;如果未选出,随机超时后进入新的阶段重试。领导者负责从客户端接收请求,并分发到其他节点; + +- + + 同步日志:领导者会决定系统中最新的日志记录,并强制所有的跟随者来刷新到这个记录,数据的同步是单向的,确保所有节点看到的视图一致。 + +此外,领导者会定期向所有跟随者发送心跳消息,跟随者如果发现心跳消息超时未收到,则可以认为领导者已经下线,尝试发起新的选举过程。 \ No newline at end of file diff --git a/C_Universal_Brockchain/markdown/21.md b/C_Universal_Brockchain/markdown/21.md new file mode 100644 index 00000000..b2a9bf3e --- /dev/null +++ b/C_Universal_Brockchain/markdown/21.md @@ -0,0 +1,105 @@ +拜占庭问题与算法 + + + +拜占庭问题(Byzantine Problem)又叫拜占庭将军(Byzantine Generals Problem)问题,讨论的是在少数节点有可能作恶(消息可能被伪造)的场景下,如何达成共识问题。拜占庭容错(Byzantine Fault Tolerant,BFT)讨论的是容忍拜占庭错误的共识算法。 + +# 两将军问题 + +拜占庭问题之前,早在 1975 年,学术界就已经开始两将军问题的讨论(《Some constraints and tradeofis in the design of network communications》):两个将军要通过信使来达成进攻还是撤退的约定,但信使可能迷路或被敌军阻拦(消息丢失或伪造),如何达成一致?这是典型的异步双方共识模型,根据 FLP 不可能原理,这个问题不存在通用解。 + +# 拜占庭问题 + +拜占庭问题最早由 Leslie Lamport 等学者于 1982 年在论文《The Byzantine Generals Problem》中正式提出,是用来解释异步系统中共识问题的一个虚构模型。拜占庭是古代东罗马帝国的首都,由于地域宽广,假设其守卫边境的多个将军(系统中的多个节点)需要通过信使来传递消息,达成某些一致决定。但由于将军中可能存在叛徒(系统中节点出错),这些叛徒将向不同的将军发送不同的消息,试图干扰共识的达成。 + +拜占庭问题即讨论在此情况下,如何让忠诚的将军们能达成行动的一致。 + +一般分布式场景下,拜占庭需求并不多见,但在特定场景下会有较大意义。例如容易匿名参与的系统(如比特币),或是出现欺诈可能造成巨大损失的情况(金融系统)。 + +# 问题的解决 + +论文中指出,对于拜占庭问题来说,假如节点总数为 N,故障节点数为 F,则当 N >= 3F + 1 时,问题才能有解,由 BFT 算法进行保证。 + +例如,N = 3,F = 1 时。 + +若提案人不是叛变者,提案人发送一个提案出来,收到的叛变者可以宣称收到的是相反的命令。则对于第三个人(忠诚者)会收到两个相反的消息,无法判断谁是叛变者,则系统无法达到一致。 + +若提案人是叛变者,发送两个相反的提案分别给另外两人,另外两人都收到两个相反的消息,无法判断究竟谁是叛变者,则系统无法达到一致。 + +更一般的,当提案人不是叛变者,提案人提出提案信息 1,则对于合作者来看,系统中会有 N - F 份确定的信息 1,和 F 份不确定的信息(可能为 0 或 1,假设叛变者会尽量干扰一致的达成),N − F > F,即 N > 2F 情况下才能达成一致。 + +当提案人是叛变者,会尽量发送相反的提案给 N - F 个合作者,从收到 1 的合作者看来,系统中会存在 (N - F)/2 个信息 1,以及 (N - F)/2 个信息 0;从收到 0 的合作者看来,系统中会存在 (N - F)/2 个信息 0,以及 (N - F)/2 个信息 1; + +另外存在 F − 1 个不确定的信息。合作者要想达成一致,必须进一步的对所获得的消息进行判定,询问其他人某个被怀疑对象的消息值,并通过取多数来作为被怀疑者的信息值。这个过程可以进一步递归下去。 + +1980 年,Leslie Lamport 等人在论文《Reaching agreement in the presence of faults》中证明,当叛变者不超过 1/3 时,存在有效的拜占庭容错算法(最坏需要 F+1 轮交互)。反之,如果叛变者过多,超过 1/3,则无法保证一定能达到一致结果。 + +那么,当存在多于 1/3 的叛变者时,有没有可能存在解决方案呢? + +设想 F 个叛变者和 L 个忠诚者,叛变者故意使坏,可以给出错误的结果,也可以不响应。某个时候 F 个叛变者都不响应,则 L 个忠诚者取多数既能得到正确结果。当 F 个叛变者都给出一个恶意的提案,并且 L 个忠诚者中有 F 个离线时,剩下的 L - F 个忠诚者此时无法分别是否混入了叛变者,仍然要确保取多数能得到正确结果,因此,L - F > F,即 L > 2F 或 N - F > 2F,所以系统整体规模 N 要大于 3F。 + +能确保达成共识的拜占庭系统节点数至少为 4,此时最多允许出现 1 个坏的节点。 + +# 拜占庭容错算法 + +拜占庭容错算法(Byzantine Fault Tolerant)是面向拜占庭问题的容错算法,解决的是在网络通信可靠,但节点可能故障和作恶情况下如何达成共识。 + +拜占庭容错算法最早的讨论可以追溯到 Leslie Lamport 等人 1982 年 发表的论文《The Byzantine Generals Problem》,之后出现了大量的改进工作,代表性成果包括《Optimal Asynchronous Byzantine Agreement》(1992 年)、《Fully Polynomial Byzantine Agreement for n>3t Processors in t+1 Rounds》(1998 年)等。长期以来,拜占庭问题的解决方案都存在运行过慢,或复杂度过高的问题,直到“实用拜占庭容错算法”(Practical Byzantine Fault Tolerance,PBFT) 算法的提出。 + +1999 年,PBFT 算法由 Miguel Castro 和 Barbara Liskov 于论文《Practical Byzantine Fault Tolerance》中提出。该算法基于前人工作(特别是 Paxos 相关算法,因此也被称为 Byzantine Paxos)进行了优化,首次将拜占庭容错算法复杂度从指数级降低到了多项式(平方)级,目前已得到广泛应用。其可以在恶意节点不超过总数 1/3 的情况下同时保证 Safety 和 Liveness。 + +PBFT 算法采用密码学相关技术(RSA 签名算法、消息验证编码和摘要)确保消息传递过程无法被篡改和破坏。 + +算法的基本过程如下: + +- + + 首先,通过轮换或随机算法选出某个节点为主节点,此后只要主节点不切换,则称为一个视图(View)。 + +- + + 在某个视图中,客户端将请求 `` 发送给主节点(如果客户端发给从节点,从节点可以转发给主节点),主节点负责广播请求到所有其它从节点并完成共识。 + +- + + 所有节点处理完成请求,将处理结果 `` 返回给客户端。客户端检查是否收到了至少 f+1 个来自不同节点的相同结果,作为最终结果。 + +主节点广播过程包括三个阶段的处理:预准备(Pre-Prepare)、准备(Prepare)和提交(Commit)。预准备和准备阶段确保在同一个视图内请求发送的顺序正确;准备和提交阶段则确保在不同视图之间的确认请求是保序的。 + +- + + 预准备阶段:主节点为从客户端收到的请求分配提案编号,然后发出预准备消息 `<,message>` 给各从节点,主节点需要对预准备消息进行签名。其中 n 是主节点为这个请求分配的序号,message 是客户端的请求消息,digest 是消息的摘要。这一步的目的是为请求分配序号并通知其他节点,因此可以不包括原始的请求消息,可以通过其他方式将请求同步到从节点。 + +- + + 准备阶段:从节点收到预准备消息后,检查消息(包括核对签名、视图、编号)。如消息合法,则向其它节点发送准备消息 ``,带上自己的 id 信息,并添加签名。收到准备消息的节点同样对消息进行合法性检查。节点集齐至少 2f+1 个验证过的消息则认为验证通过,把这个准备消息写入本地提交消息日志中。这一步是为了确认大多数节点已经对序号达成共识,本节点已经准备好进行提交了。 + +- + + 提交阶段:广播 commit 消息 `` 并添加自己签名,告诉其它节点某个编号为 n 的提案在视图 v 里已经处于提交状态。如果集齐至少 2f+1 个验证过的 commit 消息,则说明提案被整个系统接受。 + +PBFT 算法和 Raft 算法的过程十分类似。区别在于 PBFT 算法中并不假设主节点一定是可靠的,因此增加了额外的从节点之间的交互,当发现主节点不可靠时通过重新选举选出新的主节点。 + +具体实现上还包括 checkpoint(同步节点状态和清理本地日志数据)、视图切换(重新选举主节点)等机制,读者可自行参考论文内容,在此不再赘述。 + +拜占庭容错类的算法因为要考虑最恶意的存在“捣乱”者的情况,在大规模场景下共识性能往往会受到影响。 + +# 新的解决思路 + +拜占庭问题之所以难解,在于任何时候系统中都可能存在多个提案(因为提案成本很低),并且在大规模场景下要完成最终确认的过程容易受干扰,难以达成共识。 + +2014 年,斯坦福大学的 Christopher Copeland 和 Hongxia Zhong 在论文《Tangaroa: a byzantine fault tolerant raft》中提出在 Raft 算法基础上借鉴 PBFT 算法的一些特性(包括签名、恶意领导探测、选举校验等)来实现拜占庭容错性,兼顾可实现性和鲁棒性。该论文也启发了 Kadena 等项目的出现,实现更好性能的拜占庭算法。 + +2017 年,MIT 计算机科学与人工智能实验室(CSAIL)的 Yossi Gilad 和 Silvio Micali 等人在论文《Algorand: Scaling Byzantine Agreements for Cryptocurrencies》中针对 PBFT 算法在很多节点情况下性能不佳的问题,提出先选出少量记账节点,然后再利用可验证随机函数(Verifiable Random Function,VRF)来随机选取领导节点,避免全网直接做共识,将拜占庭算法扩展到了支持较大规模的应用场景,同时保持较好的性能(1000+ tps)。 + +此外,康奈尔大学的 Rafael Pass 和 Elaine Shi 在论文《The Sleepy Model of Consensus》中探讨了在动态场景(大量节点离线情况)下如何保障共识的安全性,提出的 Sleepy Consensus 算法可以在活跃诚实节点达到一半以上时确保完成拜占庭共识。 + +2018 年,清华大学的 Chenxing Li 等在论文《Scaling Nakamoto Consensus to Thousands of Transactions per Second》中提出了 Conflux 共识协议。该协议在 GHOST 算法基础上改善了安全性,面向公有区块链场景,理论上能达到 6000+ tps。 + +2019 年,康奈尔大学和 VMWare 研究院的 Maofan Yin 等在论文《HotStuff: BFT Consensus with Linearity and Responsiveness》中对 PBFT 算法进行了改进:利用主节点来简化通信量,同时将视图切换与共识操作进行统一。值得一提的是,Facebook Libra 白皮书中采用了该成果。 + +比特币网络在设计时使用了 PoW(Proof of Work)的概率型算法思路,从如下两个角度解决大规模场景下的拜占庭容错问题。 + +首先,限制一段时间内整个网络中出现提案的个数(通过工作量证明来增加提案成本);其次是丢掉最终确认的约束,约定好始终沿着已知最长的链进行拓展。共识的最终确认是概率意义上的存在。这样,即便有人试图恶意破坏,也会付出相应的经济代价(超过整体系统一半的工作量)。后来的各种 PoX 系列算法,也都是沿着这个思路进行改进,采用经济博弈来制约攻击者。 + +另外,由于要处理的场景比较苛刻,BFT 类算法的吞吐量往往不高。除了可以放宽约束外(例如通常情况下信任主节点,出现问题再回滚),还可以引入多个互不影响的主节点进行并行处理。 \ No newline at end of file diff --git a/C_Universal_Brockchain/markdown/22.md b/C_Universal_Brockchain/markdown/22.md new file mode 100644 index 00000000..940aa1a1 --- /dev/null +++ b/C_Universal_Brockchain/markdown/22.md @@ -0,0 +1,105 @@ +可靠性指标 + + + +可靠性(Availability),或者说可用性,是描述系统可以提供服务能力的重要指标。高可靠的分布式系统往往需要各种复杂的机制来进行保障。 + +通常情况下,服务的可用性可以用服务承诺(Service Level Agreement,SLA)、服务指标(Service Level Indicator,SLI)、服务目标(Service Level Objective,SLO)等方面进行衡量。 + +# 几个 9 的指标 + +完美的可靠性是不存在的。很多领域里谈到服务的高可靠性,通常都会用“几个 9”的指标来进行衡量。 + +“几个 9”的指标,其实是概率意义上粗略反映了系统能提供服务的可靠性指标,最初是电信领域提出的概念。 + +下表给出不同指标下,每年允许服务出现不可用时间的参考值。 + +指标 + +概率可靠性 + +每年允许不可用时间 + +典型场景 + +一个 9 + +90% + +1.2 个月 + +简单测试 + +二个 9 + +99% + +3.6 天 + +普通单点 + +三个 9 + +99.9% + +8.6 小时 + +普通集群 + +四个 9 + +99.99% + +51.6 分钟 + +高可用 + +五个 9 + +99.999% + +5 分钟 + +电信级 + +六个 9 + +99.9999% + +31 秒 + +极高要求 + +七个 9 + +99.99999% + +3 秒 + +N/A + +一般来说,单点的服务器系统至少应能满足两个 9;普通企业信息系统应能满足三个 9;少数领先企业(如亚马逊、甲骨文)产品能实现四个 9 甚至更多。大型金融和电信系统指标是五个 9,意味着每年最多允许出现五分钟左右的服务故障。五个 9 以上的系统十分罕见,要实现往往意味着极高的成本。 + +# 两个核心时间 + +一般地,描述系统出现故障的可能性和故障出现后的恢复能力,有两个基础的指标:MTBF 和 MTTR。 + +- + + MTBF:Mean Time Between Failures,平均故障间隔时间,即系统可以无故障运行的预期时间。 + +- + + MTTR:Mean Time To Repair,平均修复时间,即发生故障后,系统可以恢复到正常运行的预期时间。 + +MTBF 衡量了系统发生故障的频率,如果一个系统的 MTBF 很短,则往往意味着该系统可用性低;而 MTTR 则反映了系统碰到故障后服务的恢复能力,如果系统的 MTTR 过长,则说明系统一旦发生故障,需要较长时间才能恢复服务。 + +一个高可用的系统应该是具有尽量长的 MTBF 和尽量短的 MTTR。 + +# 提高可靠性 + +那么,该如何提升可靠性呢?有两个基本思路:一是让系统中的单个组件都变得更可靠;二是干脆消灭单点。 + +IT 从业人员大都有类似的经验,普通笔记本电脑,基本上是过一阵可能就要重启下;而运行 Linux/Unix 系统的专用服务器,则可能连续运行几个月甚至几年时间都不出问题。另外,普通的家用路由器,跟生产级别路由器相比,更容易出现运行故障。这些都是单个组件可靠性不同导致的例子,可以通过简单升级单点的软硬件来改善可靠性。 + +然而,依靠单点实现的可靠性毕竟是有限的。要想进一步的提升,那就只好消灭单点,通过主从、多活等模式让多个节点集体完成原先单点的工作。这可以从概率意义上改善服务对外整体的可靠性,这也是分布式系统的一个重要用途。 \ No newline at end of file diff --git a/C_Universal_Brockchain/markdown/23.md b/C_Universal_Brockchain/markdown/23.md new file mode 100644 index 00000000..667c8f43 --- /dev/null +++ b/C_Universal_Brockchain/markdown/23.md @@ -0,0 +1,1193 @@ +# fabric-sdk 作用 + +[toc] + +# 一、SDK 提供的功能 + +在之前的操作中 无论是创建 channel 还是 链码 安装实例化 合约的查询 调用 在 CA 中注册用户,都是通过[命令行](https://so.csdn.net/so/search?q=命令行&spm=1001.2101.3001.7020)的形式去操作的。 +sdk 提供了一系列的功能,可以通过 sdk 去实现之前使用命令行操作的效果。 +现阶段 fabric 提供的比较稳定的 sdk 有:[Node](https://so.csdn.net/so/search?q=Node&spm=1001.2101.3001.7020)、java、go +如果是创建一个区块链管理平台。就更需要 SDK 来获取到区块链中的信息。 + +总之,业务系统想要与 fabric 工程交互就应该使用 sdk。 + + + +# 二、使用 java-fabric-sdk + +创建一个 springboot 项目 pom 中引用对应版本的 sdk + +``` + + + org.hyperledger.fabric-sdk-java + fabric-sdk-java + 1.4.6 + +``` + + + +# 三、使用 sdk 创建 channel 并将 peer 加入到 channel 中 + +创建 channel 需要使用 channel 配置文件中指定在 Channel 中的 org 的 Admin 用户。 +创建 channel 需要生成创世区块,这一步需要有 orderer 的配置信息 + +所以 使用 sdk 创建 channel 要准备一下的配置信息 + +- channel 中任意一个 org 的 admin 用户信息 +- orderer 节点的信息 + + + +## 3.1 启动 first-network + +使用 fabric-samples 提供的 byfn.sh 脚本启动 first-network + +``` +./byfn.sh uo -s couchdb +``` + +启动完成后会在 first-network 目录下生成两个目录 + +``` +crypto-config : 存放了orderer的证书和peer的证书 +channel-artifacts:存放了channel的配置和peer锚节点的配置以及channel的创世区块 +``` + + + +## 3.2 生成 channel 的配置文件 + +使用 first-network 中提供的 configtx.yaml 配置文件, 配置文件中有关于 channel 的一个配置。 + +``` +TwoOrgsChannel: + Consortium: SampleConsortium + <<: *ChannelDefaults + Application: + <<: *ApplicationDefaults + Organizations: + - *Org1 + - *Org2 + Capabilities: + <<: *ApplicationCapabilities +``` + +按照这个配置文件的描述,将会生成一个 SampleConsortium 联盟,联盟中包含 Org1 和 Org2 两个组织 +使用 configtxgen 工具 生成 channel 的配置文件 + +``` +configtxgen -profile TwoOrgsChannel -outputCreateChannelTx channel-artifacts/mychannel1.tx -channelID mychannel1 +``` + +以上命令中的几个参数 + +``` +-profile 使用配置文件中Profiles下的那个配置项,这里用到的Profiles.TwoOrgsChannel + -outputCreateChannelTx 执行最终生成的channel的tx配置文件的位置 + -channelID 使用这个configtx的channel的channelId + -configPath 指定包含configtx.yaml配置文件的目录(如果命令执行的目录就包含configtx.yaml则不需要使用该参数) +``` + +执行完命令后在指定的目录下会出现 mychannel1.tx 配置文件。 + + + +## 3.3 创建一个 springboot 项目 + +通过 maven 引入 fabric-sdk-java + +``` + + + org.hyperledger.fabric-sdk-java + fabric-sdk-java + 1.4.6 + +``` + + + +将上一步生成的 channel1.tx 和联盟中的证书都复制到 springboot 的 resources 目录下 + +![img](C:\Users\smile\Desktop\区块链\C_Universal_Brockchain\image\watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMwMTEwNDM1,size_16,color_FFFFFF,t_70.png) +为了方便从 resources 目录下读取文件编写一个 utils 类 + +``` +import org.springframework.core.io.ClassPathResource; + +import java.io.File; + +public class ClasspathFileUtils { + /** + * 在springboot的resources目录下 获取文件 + * + * @param resPath + * @return + * @throws Exception + */ + public static File getFileFromSpringBootClassPath(String resPath) throws Exception { + ClassPathResource classPathResource = new ClassPathResource(resPath); + return classPathResource.getFile(); + } +} +``` + + + +## 3.4 修改 application.properties 文件 + +``` +# 配置用户信息 +fabric.user.name=admin +fabric.user.account=LH +fabric.user.affiliation=Org1 +fabric.user.msp-id=Org1MSP + +# 配置orderer信息 +fabric.orderer.name=orderer.example.com +fabric.orderer.grpcs-addr=grpcs://orderer.example.com:7050 +fabric.orderer.tlsca-cert=crypto-config/ordererOrganizations/example.com/tlsca/tlsca.example.com-cert.pem +fabric.orderer.ca-cert=crypto-config/ordererOrganizations/example.com/ca/ca.example.com-cert.pem + +# 配置channel信息 +fabric.channel.channel-name=mychannel1 +fabric.channel.channel-config-tx-path=channel-artifacts/mychannel1.tx + +# 配置org1-peer0的信息 +fabric.org1-peer0.name=peer0.org1.example.com +fabric.org1-peer0.grpcs-addr=grpcs://peer0.org1.example.com:7051 +fabric.org1-peer0.tlsca-cert=crypto-config/peerOrganizations/org1.example.com/msp/tlscacerts/tlsca.org1.example.com-cert.pem +fabric.org1-peer0.users-admin-private-key=crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/keystore/2b33de36c48cc20ff8056c525db5ddfc3b3cbfe337984e867294de19fc3a770b_sk +fabric.org1-peer0.users-admin-cert=crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/admincerts/Admin@org1.example.com-cert.pem +``` + + + +编写配置文件对应的配置类 + +![img](C:\Users\smile\Desktop\区块链\C_Universal_Brockchain\image\watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMwMTEwNDM1,size_16,color_FFFFFF,t_70-16537372953451.png) + +``` +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import java.util.Set; +@Getter +@Setter +@ConfigurationProperties(prefix = "fabric.user") +public class FabricUserProperties { + private String name; + private String account; + private String affiliation; + private String mspId; + private Set roles; +} +``` + +``` +@Getter +@Setter +@ConfigurationProperties(prefix = "fabric.channel") +public class FabricChannelProperties { + private String channelName; + private String channelConfigTxPath; +} +``` + + + +``` +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +@Getter +@Setter +@ConfigurationProperties(prefix = "fabric.orderer") +public class FabricOrdererProperties { + private String name; + private String grpcsAddr; + private String tlscaCert; + private String caCert; +} +``` + + + +``` +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +@Getter +@Setter +@ConfigurationProperties(prefix = "fabric.org1-peer0") +public class FabricOrg1Peer0Properties { + private String name; + private String grpcsAddr; + private String tlscaCert; + private String usersAdminPrivateKey; + private String usersAdminCert; +} +``` + + + +在启动类上声明使用的配置类 + + + +``` +@EnableConfigurationProperties({ + FabricUserProperties.class, + FabricOrdererProperties.class, + FabricChannelProperties.class, + FabricOrg1Peer0Properties.class +}) +@SpringBootApplication +public class JavaFabricSdkApplication { + public static void main(String[] args) { + SpringApplication.run(JavaFabricSdkApplication.class, args); + } +} +``` + + + +## 3.5 编写 Fabric 用户信息实现 Fabric User 接口 + + + +``` +import com.lhit.fabric.javafabricsdk.fabric.properties.FabricOrg1Peer0Properties; +import com.lhit.fabric.javafabricsdk.fabric.properties.FabricUserProperties; +import com.lhit.fabric.javafabricsdk.fabric.util.UserUtils; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.hyperledger.fabric.sdk.Enrollment; +import org.hyperledger.fabric.sdk.User; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import javax.annotation.PostConstruct; +import java.security.Security; +import java.util.Set; + +/** + * 在联盟网络中的用户信息 + */ +@Slf4j +@Getter +@Setter +@Component +public class FabricUserContext implements User { + + @Autowired + private FabricUserProperties userProperties; + + @Autowired + private FabricOrg1Peer0Properties org1Peer0Properties; + + @PostConstruct + private void init() { + log.info(">>>>>>>>>>>>>>>>>>>>>>>>>>加载FabricUserContext"); + // 指定下加密算法 否则节点通讯过程会报错 + Security.addProvider(new BouncyCastleProvider()); + } + + @Override + public String getName() { + return userProperties.getName(); + } + + @Override + public Set getRoles() { + return userProperties.getRoles(); + } + + @Override + public String getAccount() { + return userProperties.getAccount(); + } + + @Override + public String getAffiliation() { + return userProperties.getAffiliation(); + } + + @Override + public Enrollment getEnrollment() { + try { + // 这里用到了org1 peer0节点的 私钥和证书。用于用户enroll到节点中 + Enrollment enrollment = UserUtils.getEnrollment(org1Peer0Properties.getUsersAdminPrivateKey(), org1Peer0Properties.getUsersAdminCert()); + return enrollment; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + @Override + public String getMspId() { + return userProperties.getMspId(); + } +} +``` + + + +为了方便读取证书信息封装的工具类 + + + +``` +import org.bouncycastle.crypto.CryptoException; +import org.hyperledger.fabric.sdk.Enrollment; +import javax.xml.bind.DatatypeConverter; +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; + +/** + * 用户工具类用于读取证书和私钥信息到java对象中 + */ +public class UserUtils { + + private static class CAEnrollment implements Enrollment { + + private PrivateKey key; + private String ecert; + + public CAEnrollment(PrivateKey key, String ecert) { + this.key = key; + this.ecert = ecert; + } + + @Override + public PrivateKey getKey() { + return key; + } + + @Override + public String getCert() { + return ecert; + } + } + + /** + * @param keyPath 私钥的地址 crypto-config/ordererOrganizations/example.com/ca/ca.example.com-cert.pem + * @param certPath 证书的地址 crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/admincerts/Admin@org1.example.com-cert.pem + * @return enrollment 带有用户信息的对象 + * @throws IOException + * @throws NoSuchAlgorithmException + * @throws CryptoException + * @throws InvalidKeySpecException + * @description 根据证书目录和私钥目录读取到enrollment里面。 + */ + public static Enrollment getEnrollment(String keyPath, String certPath) throws Exception { + PrivateKey key = null; + String certificate = null; + InputStream isKey = null; + BufferedReader brKey = null; + try { + isKey = new FileInputStream(ClasspathFileUtils.getFileFromSpringBootClassPath(keyPath)); + brKey = new BufferedReader(new InputStreamReader(isKey)); + StringBuilder keyBuilder = new StringBuilder(); + for (String line = brKey.readLine(); line != null; line = brKey.readLine()) { + if (line.indexOf("PRIVATE") == -1) { + keyBuilder.append(line); + } + } + certificate = new String(Files.readAllBytes(Paths.get(ClasspathFileUtils.getFileFromSpringBootClassPath(certPath).getPath()))); + byte[] encoded = DatatypeConverter.parseBase64Binary(keyBuilder.toString()); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded); + KeyFactory kf = KeyFactory.getInstance("ECDSA"); + key = kf.generatePrivate(keySpec); + } finally { + isKey.close(); + brKey.close(); + } + return new CAEnrollment(key, certificate); + } +} +``` + + + +## 3.6 编写 FabricClient 用于与网络的交互 + + + +``` +import com.lhit.fabric.javafabricsdk.fabric.properties.FabricChannelProperties; +import com.lhit.fabric.javafabricsdk.fabric.properties.FabricOrdererProperties; +import com.lhit.fabric.javafabricsdk.fabric.properties.FabricOrg1Peer0Properties; +import com.lhit.fabric.javafabricsdk.fabric.user.FabricUserContext; +import com.lhit.fabric.javafabricsdk.fabric.util.ClasspathFileUtils; +import lombok.extern.slf4j.Slf4j; +import org.hyperledger.fabric.sdk.*; +import org.hyperledger.fabric.sdk.security.CryptoSuite; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.util.Properties; + +/** + * 创建Channel使用到的client + */ +@Slf4j +@Component +public class FabricClient { + + @Autowired + private FabricUserContext userContext; + + // Hyperledger Fabric Client 用于创建Channel + private HFClient hfClient; + + @Autowired + private FabricChannelProperties channelProperties; + + @Autowired + private FabricOrdererProperties ordererProperties; + + @Autowired + private FabricOrg1Peer0Properties org1Peer0Properties; + + private Orderer orderer; + + private Peer org1Peer0; + + @PostConstruct + private void init() throws Exception { + log.info(">>>>>>>>>>>>>>>>>>>>>>>>>>加载FabricClient"); + // 创建客户端 + hfClient = HFClient.createNewInstance(); + // 指定加密算法 + CryptoSuite cryptoSuite = CryptoSuite.Factory.getCryptoSuite(); + hfClient.setCryptoSuite(cryptoSuite); + // 指定用户身份 + hfClient.setUserContext(userContext); + } + + /** + * 创建channel + * + */ + public Channel createChannel() throws Exception { + + // channel的配置信息 + ChannelConfiguration channelConfiguration = new ChannelConfiguration(ClasspathFileUtils.getFileFromSpringBootClassPath(channelProperties.getChannelConfigTxPath())); + // 创建channel 需要的参数 + // channelName channel的名称 + // orderer orderer节点对象 + // channelConfiguration channel的配置信息 + // channelConfigurationSignature 用户签名信息 + Channel channel = hfClient.newChannel(channelProperties.getChannelName(), getOrderer(), channelConfiguration, hfClient.getChannelConfigurationSignature(channelConfiguration, hfClient.getUserContext())); + channel.initialize(); + return channel; + } + + /** + * 获取orderer对象 + * + * @return + * @throws Exception + */ + public Orderer getOrderer() throws Exception { + if (orderer == null) { + String path = ClasspathFileUtils.getFileFromSpringBootClassPath(ordererProperties.getTlscaCert()).getPath(); + Properties properties = new Properties(); + properties.setProperty("pemFile", path); + Orderer orderer = hfClient.newOrderer(ordererProperties.getName(), ordererProperties.getGrpcsAddr(), properties); + return orderer; + } + return orderer; + } + + /** + * 获取peer对象 + * + * @return + * @throws Exception + */ + public Peer getOrg1Peer0() throws Exception { + if (org1Peer0 == null) { + String path = ClasspathFileUtils.getFileFromSpringBootClassPath(org1Peer0Properties.getTlscaCert()).getPath(); + Properties properties = new Properties(); + properties.setProperty("pemFile", path); + Peer peer = hfClient.newPeer(org1Peer0Properties.getName(), org1Peer0Properties.getGrpcsAddr(), properties); + return peer; + } + return org1Peer0; + } + + /** + * 根据channel名称获取channel + * + * @param channelName + * @return + * @throws Exception + */ + public Channel getChannel(String channelName) throws Exception { + Channel channel = hfClient.getChannel(channelName); + if (channel == null) { + channel = hfClient.newChannel(channelName); + } + channel.addOrderer(getOrderer()); + return channel; + } +} +``` + + + +## 3.7 编写 Service 来提供与 Fabric 网络交互的能力 + + + +``` +import com.lhit.fabric.javafabricsdk.fabric.client.FabricClient; +import org.hyperledger.fabric.sdk.Channel; +import org.hyperledger.fabric.sdk.Peer; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class FabricChannelService { + + @Autowired + private FabricClient fabricClient; + + + /** + * 创建通道 + * + * @throws Exception + */ + public Channel createChannel() throws Exception { + return fabricClient.createChannel(); + } + + /** + * 加入通道 + * + * @throws Exception + */ + public void joinChannel(Channel channel,Peer peer) throws Exception { + channel.joinPeer(peer); + channel.initialize(); + } + + public Channel getChannel(String channelName) throws Exception { + return fabricClient.getChannel(channelName); + } +} +``` + + + +## 3.8 完整的目录结构 + + + + + +![img](C:\Users\smile\Desktop\区块链\C_Universal_Brockchain\image\watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMwMTEwNDM1,size_16,color_FFFFFF,t_70-16537372953452.png) + + + + + +## 3.9 编写测试类 测试是否可以成功创建 channel 并将节点 org1Peer0 加入到 channel 中 + + + +``` +@Slf4j +@SpringBootTest +class JavaFabricSdkApplicationTests { + + + @Autowired + private FabricChannelService channelService; + + @Autowired + private FabricClient fabricClient; + + @Test + void contextLoads() throws Exception { + + log.info("开始创建channel"); + Channel channel = channelService.createChannel(); + + log.info("org1peer0节点 加入channel"); + channelService.joinChannel(channel,fabricClient.getOrg1Peer0()); + + log.info("完成创建channel并将org1peer0加入到channel中"); + } + +} +``` + + + +成功执行完毕后 ,如果没有报错 到 docker 容器中验证 org1 peer0 节点是否加入到了 channel 中 + + + +``` +# 进入到cli容器中 +docker exec -it cli bash +# cli默认的用户身份就是 org1 peer0 admin用户所以可以直接查看当前节点加入的channel +peer channel list +# 输出 说明 成功的通过java sdk向fabric网络中添加了channel和并将peer加入到了channel中 +root@dbe359f6631b:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer channel list +2020-04-01 07:28:46.769 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized +Channels peers has joined: +mychannel +mychannel1 +``` + + + +# 四、使用 java sdk 安装链码到 peer 节点中 + + + +## 4.1 在 FabricChannelService 中添加安装链码的方法 + + + +``` +/** + * 安装链码 + * + * 注意:目录 {chaincodeLoaction}/src/{chaincodePath}/ + * + * 在当前项目中 就是 + * chaincodeLoaction = chaincode + * chaincodePath = basic_info + * + * @param type 链码语言GO JAVA NODE + * @param chaincodeName 链码名称 + * @param version 链码版本 + * @param chaincodeLoaction 链码路径 + * @param chaincodePath 链码路径 + * @param peers 要安装到哪些节点上(这些节点必须在同一个org内) + * @throws Exception + */ + public Collection installChaincode(TransactionRequest.Type type, String chaincodeName, String version,String chaincodeLoaction ,String chaincodePath, List peers) throws Exception { + // 初始化Install对象 + InstallProposalRequest installProposalRequest = fabricClient.getHfClient().newInstallProposalRequest(); + // 生成chaincodeId + ChaincodeID chaincodeID = ChaincodeID.newBuilder().setName(chaincodeName).setVersion(version).build(); + // 设置链码语言类型 + installProposalRequest.setChaincodeLanguage(type); + // 设置chaincodeId + installProposalRequest.setChaincodeID(chaincodeID); + // 指定链码源文件 + installProposalRequest.setChaincodeSourceLocation(ClasspathFileUtils.getFileFromSpringBootClassPath(chaincodeLoaction)); + // 设置chanincodePata + installProposalRequest.setChaincodePath(chaincodePath); + return fabricClient.getHfClient().sendInstallProposal(installProposalRequest, peers); + } +``` + + + +编写测试用例 + + + +``` +@Test + void testInstallChaincodeToPeer() throws Exception { + Collection basicinfo = channelService.installChaincode(TransactionRequest.Type.GO_LANG, + "basicinfo", + "1.0", + "chaincode", + "basic_info", + Lists.newArrayList(fabricClient.getOrg1Peer0())); + } +``` + + + +测试完成后在容器中查看 Org1Peer0 是否已经安装了链码 + + + +``` +# 进入容器 +docker exec -it cli bash + +# 执行查看链码安装列表命令 +peer chaincode list --installed + +# 输出 +Get installed chaincodes on peer: +Name: basicinfo, Version: 1.0, Path: basic_info, Id: 124f873c5b5ffa6b57fa2dbb55c463bff7cae916ef297d2d78c6f345126714a2 +Name: mycc, Version: 1.0, Path: github.com/chaincode/chaincode_example02/go/, Id: 333a19b11063d0ade7be691f9f22c04ad369baba15660f7ae9511fd1a6488209 +``` + + + +# 五、使用 java sdk 实例化链码 + + + +在 service 中添加实例化链码的方法, 在实例化链码时需要制定背书策略。背书策略一般使用 yaml 文件来描述 + + + +``` +# 背书策略描述文件 +identities: # 指定参与背书的角色身份 因为代码中只是将链码安装实例化到了org1的peer0节点,所以先只写user1 + user1: {"role":{"name":"member","mspId":"Org1MSP"}} +# user2: {"role":{"name":"member","mspId":"Org2MSP"}} +policy: # 具体策略 + 1-of: # 以下声明中的一个 就是 user1 背书即可 + - signed-by: "user1" +# - signed-by: "user2" +# 2-of: # 以下声明中的两个 就是 user1 user2 都需要背书即可 +# - signed-by: "user1" +# - signed-by: "user2" +# 1-of: # 以下声明中的任意一个 就是 user1 user2 其中一个背书即可 +# - signed-by: "user1" +# - signed-by: "user2" +``` + + + +把文件复制到 resources 目录下 + +![img](C:\Users\smile\Desktop\区块链\C_Universal_Brockchain\image\watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMwMTEwNDM1,size_16,color_FFFFFF,t_70-16537372953453.png) + + + + + +``` +/** + * 实例化链码 + * + * @param type 链码语言类型 + * @param channelName channel名称 + * @param chaincodeName 链码名称 + * @param version 版本 + * @param orderer orderer节点信息 + * @param peer 要安装到那个peer + * @param funcName 初始化方法名 如果没有可以填写任意字符串 + * @param args 初始化方法传参 如果没有 也要传个 String[] args = {""} + * @throws Exception + */ + public void instantiateChaincode(TransactionRequest.Type type, String channelName, String chaincodeName, String version, Orderer orderer, Peer peer, String funcName, String[] args) throws Exception { + + // 获取到channel + Channel channel = getChannel(channelName); + // 指定channel中的 orderer和peer + channel.addPeer(peer); + channel.addOrderer(orderer); + // 初始化channel + channel.initialize(); + + //构造提案 + InstantiateProposalRequest instantiateProposalRequest = fabricClient.getHfClient().newInstantiationProposalRequest(); + // 设置语言 + instantiateProposalRequest.setChaincodeLanguage(type); + + // 生成chaincodeId + ChaincodeID chaincodeID = ChaincodeID.newBuilder().setName(chaincodeName).setVersion(version).build(); + instantiateProposalRequest.setChaincodeID(chaincodeID); + // 设置初始化方法和参数 + instantiateProposalRequest.setFcn(funcName); + instantiateProposalRequest.setArgs(args); + + // 设置背书策略 + ChaincodeEndorsementPolicy chaincodeEndorsementPolicy = new ChaincodeEndorsementPolicy(); + chaincodeEndorsementPolicy.fromYamlFile(ClasspathFileUtils.getFileFromSpringBootClassPath("endorsement_policy/my_endorsement_policy.yaml")); + // 背书策略设置到提案中 + instantiateProposalRequest.setChaincodeEndorsementPolicy(chaincodeEndorsementPolicy); + + // 合约实例化 用channel 提交提案 + Collection proposalResponses = channel.sendInstantiationProposal(instantiateProposalRequest); + for (ProposalResponse proposalRespons : proposalResponses) { + if (proposalRespons.getStatus().getStatus() != 200) { + throw new Exception("提案返回报错"); + } + } + + // 提交 数据到链上 + channel.sendTransaction(proposalResponses); + + } +``` + + + +添加测试方法 + + + +``` +@Test + void testInstantiateChaincode() throws Exception { + log.info("开始实例化链码"); + String[] args = {""}; + channelService.instantiateChaincode(TransactionRequest.Type.GO_LANG, + channelProperties.getChannelName(), + "basicinfo", + "1.0", + fabricClient.getOrderer(), + fabricClient.getOrg1Peer0(), + "", + args + ); + log.info("完成实例化链码"); + } +``` + + + +测试通过后检查对应节点上已经实例化的链码 + + + +``` +# 进入cli容器 +docker exec -it cli bash +# 查看当前节点已经实例化的链码 +peer chaincode list --instantiated -C mychannel1 +# 输出channel中已经实例化的链码 +Get instantiated chaincodes on channel mychannel1: +Name: basicinfo, Version: 1.0, Path: basic_info, Escc: escc, Vscc: vscc +``` + + + +# 六、更新链码 + + + +在 service 中增加更新链码的方法 + + + +``` +/** + * 更新升级链码 + * + * @param type 链码语言类型 + * @param channelName channel名称 + * @param chaincodeName 链码名称 + * @param version 版本 + * @param orderer orderer节点信息 + * @param peer 要安装到那个peer + * @param funcName 初始化方法名 如果没有可以填写任意字符串 + * @param args 初始化方法传参 如果没有 也要传个 String[] args = {""} + * @throws Exception + */ + public void upgradeChaincode(TransactionRequest.Type type, String channelName, String chaincodeName, String version, Orderer orderer, Peer peer, String funcName, String[] args) throws Exception { + + // 获取到channel + Channel channel = getChannel(channelName); + // 指定channel中的 orderer和peer + channel.addPeer(peer); + channel.addOrderer(orderer); + // 初始化channel + channel.initialize(); + + //构造提案 + UpgradeProposalRequest upgradeProposalRequest = fabricClient.getHfClient().newUpgradeProposalRequest(); + // 设置语言 + upgradeProposalRequest.setChaincodeLanguage(type); + + // 生成chaincodeId + ChaincodeID chaincodeID = ChaincodeID.newBuilder().setName(chaincodeName).setVersion(version).build(); + upgradeProposalRequest.setChaincodeID(chaincodeID); + // 设置初始化方法和参数 + upgradeProposalRequest.setFcn(funcName); + upgradeProposalRequest.setArgs(args); + + // 修改背书策略 + ChaincodeEndorsementPolicy chaincodeEndorsementPolicy = new ChaincodeEndorsementPolicy(); + chaincodeEndorsementPolicy.fromYamlFile(ClasspathFileUtils.getFileFromSpringBootClassPath("endorsement_policy/my_endorsement_policy.yaml")); + // 背书策略设置到提案中 + upgradeProposalRequest.setChaincodeEndorsementPolicy(chaincodeEndorsementPolicy); + + + // 合约实例化 用channel 提交提案 + Collection proposalResponses = channel.sendUpgradeProposal(upgradeProposalRequest); + for (ProposalResponse proposalRespons : proposalResponses) { + if (proposalRespons.getStatus().getStatus() != 200) { + throw new Exception("提案返回报错"); + } + } + + // 提交 数据到链上 + channel.sendTransaction(proposalResponses); + } +``` + + + +编写测试方法 +要想更新链码到 2.0 版本 就需要先将 2.0 版的链码安装到 peer 上。 + + + +``` +@Test + void testUpgradeChaincode() throws Exception { + log.info("开始更新链码"); + + // 先安装2.0合约 + Collection basicinfo = channelService.installChaincode(TransactionRequest.Type.GO_LANG, + "basicinfo", + "2.0", + "chaincode", + "basic_info", + Lists.newArrayList(fabricClient.getOrg1Peer0())); + + System.out.println("end"); + + // 更新链码到2.0 + String[] args = {""}; + channelService.upgradeChaincode(TransactionRequest.Type.GO_LANG, + channelProperties.getChannelName(), + "basicinfo", + "2.0", + fabricClient.getOrderer(), + fabricClient.getOrg1Peer0(), + "", + args + ); + log.info("完成更新链码"); + } +``` + + + +执行完成后 查看 channel 中 peer 已经实例化的链码 + + + +``` +# 进入cli容器 +docker exec -it cli bash +# 查看已经实例化的链码 +peer chaincode list --instantiated -C mychannel1 +# 输出 +Get instantiated chaincodes on channel mychannel1: +Name: basicinfo, Version: 2.0, Path: basic_info, Escc: escc, Vscc: vscc +``` + + + +可以看到 basicinfo 链码的 version 已经是 2.0 了 + + + +# 七、 调用链码 - invoke + + + +在 service 中增加调用链码的方法 + + + +``` +/** + * 调用链码invoke链码 + * + * @param type 链码语言类型 + * @param channelName channel名称 + * @param chaincodeName 链码名称 + * @param version 版本 + * @param orderer orderer节点信息 + * @param peers 需要哪些peer背书 + * @param funcName 调用方法名 + * @param args 方法传参 + * @throws Exception + */ + public CompletableFuture invokeChaincode(TransactionRequest.Type type, String channelName, String chaincodeName, String version, Orderer orderer,List peers, String funcName, String[] args) throws Exception { + + // 获取到channel + Channel channel = getChannel(channelName); + for (Peer peer : peers) { + channel.addPeer(peer); + } + channel.addOrderer(orderer); + // 初始化channel + channel.initialize(); + + // 构建交易提案 + TransactionProposalRequest proposalRequest = fabricClient.getHfClient().newTransactionProposalRequest(); + // 指定语言 + proposalRequest.setChaincodeLanguage(TransactionRequest.Type.GO_LANG); + // 生成chaincodeId + ChaincodeID chaincodeID = ChaincodeID.newBuilder().setName(chaincodeName).setVersion(version).build(); + proposalRequest.setChaincodeID(chaincodeID); + // 设置初始化方法和参数 + proposalRequest.setFcn(funcName); + proposalRequest.setArgs(args); + + // 发送提案 peers要与背书规则相匹配,需要哪个peer背书 就将那个peer添加进去 + Collection proposalResponses = channel.sendTransactionProposal(proposalRequest); + // 合约实例化 用channel 提交提案 + for (ProposalResponse proposalRespons : proposalResponses) { + if (proposalRespons.getStatus().getStatus() != 200) { + throw new Exception("提案返回报错:" + proposalRespons.getMessage()); + } else { + log.info("调用:{} 链码方法成功",funcName); + } + } + + // 发送数据到链上 + return channel.sendTransaction(proposalResponses); + + } +``` + + + +编写测试方法 + + + +``` +@Test + void testInvokeChaincode() throws Exception { + log.info("开始调用链码方法"); +// {"identity":"110115","mobile":"18910012222","name":"zhangsan"} + String[] args = {"110115", "{\"name\":\"zhangsan-5.0\",\"identity\":\"110115\",\"mobile\":\"18910012222\"}"}; + CompletableFuture completableFuture = channelService.invokeChaincode(TransactionRequest.Type.GO_LANG, + channelProperties.getChannelName(), + "basicinfo", + "2.0", + fabricClient.getOrderer(), + Lists.newArrayList(fabricClient.getOrg1Peer0()), + "save", + args + ); + log.info("完成链码"); + } +``` + + + +完成测试后 到 cli 容器中验证下 + + + +``` +# 进入cli容器 +docker exec -it cli bash + +# 查询保存的数据 +peer chaincode query -C mychannel4 -n basicinfo -c '{"Args":["query","110115"]}' +# 输出 +{"identity":"110115","mobile":"18910012222","name":"zhangsan-5.0"} +``` + + + +# 八 、使用 sdk 提供的查询功能 + + + +在 service 中添加查询方法 + + + +``` +/** + * 调用链码query链码 + * + * @param channelName channel名称 + * @param chaincodeName 链码名称 + * @param funcName 调用方法名 + * @param args 方法传参 + * @throws Exception + */ + public ProposalResponse queryChaincode(TransactionRequest.Type type,String channelName, String chaincodeName, List peers, String funcName, String[] args) throws Exception { + // 获取到channel + Channel channel = getChannel(channelName); + // 多个peer 可以防止单点故障 + for (Peer peer : peers) { + channel.addPeer(peer); + } + // 初始化channel + channel.initialize(); + + + // 构建交易提案 + QueryByChaincodeRequest queryByChaincodeRequest = fabricClient.getHfClient().newQueryProposalRequest(); + // 指定语言 + queryByChaincodeRequest.setChaincodeLanguage(type); + // 生成chaincodeId + ChaincodeID chaincodeID = ChaincodeID.newBuilder().setName(chaincodeName).build(); + queryByChaincodeRequest.setChaincodeID(chaincodeID); + + // 设置初始化方法和参数 + queryByChaincodeRequest.setFcn(funcName); + queryByChaincodeRequest.setArgs(args); + + // 调用查询方法 + Collection proposalResponses = channel.queryByChaincode(queryByChaincodeRequest); + + + // 合约实例化 用channel 提交提案 + for (ProposalResponse proposalRespons : proposalResponses) { + if (proposalRespons.getStatus().getStatus() != 200) { + log.info("提案返回报错:" + proposalRespons.getMessage()); + } else { + log.info("调用:{} 链码方法成功返回数据:{}", funcName,proposalRespons.getProposalResponse().getPayload()); + return proposalRespons; + } + } + + // 如果没有成功提案 + return null; + } +``` + +编写测试方法 + +``` +@Test + void testQueryChaincode() throws Exception { + log.info("开始调用查询链码方法"); +// {"identity":"110115","mobile":"18910012222","name":"zhangsan"} + String[] args = {"110115"}; + ProposalResponse proposalResponse = channelService.queryChaincode(TransactionRequest.Type.GO_LANG, + channelProperties.getChannelName(), + "basicinfo", + Lists.newArrayList(fabricClient.getOrg1Peer0()), + "query", + args + ); + + byte[] chaincodeActionResponsePayload = proposalResponse.getChaincodeActionResponsePayload(); + log.info("完成查询链码:"+ new String(chaincodeActionResponsePayload,"UTF-8") ); + } +``` + + + +测试执行输出 + +``` +完成查询链码:{"identity":"110115","mobile":"18910012222","name":"zhangsan-5.0"} +``` + +说明已经成功从 peer 上查询到了数据 + diff --git a/C_Universal_Brockchain/24.md b/C_Universal_Brockchain/markdown/24.md similarity index 100% rename from C_Universal_Brockchain/24.md rename to C_Universal_Brockchain/markdown/24.md diff --git a/C_Universal_Brockchain/25.md b/C_Universal_Brockchain/markdown/25.md similarity index 100% rename from C_Universal_Brockchain/25.md rename to C_Universal_Brockchain/markdown/25.md diff --git a/C_Universal_Brockchain/26.md b/C_Universal_Brockchain/markdown/26.md similarity index 100% rename from C_Universal_Brockchain/26.md rename to C_Universal_Brockchain/markdown/26.md diff --git a/C_Universal_Brockchain/27.md b/C_Universal_Brockchain/markdown/27.md similarity index 100% rename from C_Universal_Brockchain/27.md rename to C_Universal_Brockchain/markdown/27.md diff --git a/C_Universal_Brockchain/28.md b/C_Universal_Brockchain/markdown/28.md similarity index 100% rename from C_Universal_Brockchain/28.md rename to C_Universal_Brockchain/markdown/28.md diff --git a/C_Universal_Brockchain/29.md b/C_Universal_Brockchain/markdown/29.md similarity index 100% rename from C_Universal_Brockchain/29.md rename to C_Universal_Brockchain/markdown/29.md diff --git a/C_Universal_Brockchain/3.md b/C_Universal_Brockchain/markdown/3.md similarity index 100% rename from C_Universal_Brockchain/3.md rename to C_Universal_Brockchain/markdown/3.md diff --git a/C_Universal_Brockchain/30.md b/C_Universal_Brockchain/markdown/30.md similarity index 100% rename from C_Universal_Brockchain/30.md rename to C_Universal_Brockchain/markdown/30.md diff --git a/C_Universal_Brockchain/31.md b/C_Universal_Brockchain/markdown/31.md similarity index 100% rename from C_Universal_Brockchain/31.md rename to C_Universal_Brockchain/markdown/31.md diff --git a/C_Universal_Brockchain/32.md b/C_Universal_Brockchain/markdown/32.md similarity index 100% rename from C_Universal_Brockchain/32.md rename to C_Universal_Brockchain/markdown/32.md diff --git a/C_Universal_Brockchain/33.md b/C_Universal_Brockchain/markdown/33.md similarity index 100% rename from C_Universal_Brockchain/33.md rename to C_Universal_Brockchain/markdown/33.md diff --git a/C_Universal_Brockchain/34.md b/C_Universal_Brockchain/markdown/34.md similarity index 100% rename from C_Universal_Brockchain/34.md rename to C_Universal_Brockchain/markdown/34.md diff --git a/C_Universal_Brockchain/35.md b/C_Universal_Brockchain/markdown/35.md similarity index 100% rename from C_Universal_Brockchain/35.md rename to C_Universal_Brockchain/markdown/35.md diff --git a/C_Universal_Brockchain/36.md b/C_Universal_Brockchain/markdown/36.md similarity index 100% rename from C_Universal_Brockchain/36.md rename to C_Universal_Brockchain/markdown/36.md diff --git a/C_Universal_Brockchain/37.md b/C_Universal_Brockchain/markdown/37.md similarity index 100% rename from C_Universal_Brockchain/37.md rename to C_Universal_Brockchain/markdown/37.md diff --git a/C_Universal_Brockchain/38.md b/C_Universal_Brockchain/markdown/38.md similarity index 100% rename from C_Universal_Brockchain/38.md rename to C_Universal_Brockchain/markdown/38.md diff --git a/C_Universal_Brockchain/39.md b/C_Universal_Brockchain/markdown/39.md similarity index 100% rename from C_Universal_Brockchain/39.md rename to C_Universal_Brockchain/markdown/39.md diff --git a/C_Universal_Brockchain/4.md b/C_Universal_Brockchain/markdown/4.md similarity index 100% rename from C_Universal_Brockchain/4.md rename to C_Universal_Brockchain/markdown/4.md diff --git a/C_Universal_Brockchain/40.md b/C_Universal_Brockchain/markdown/40.md similarity index 100% rename from C_Universal_Brockchain/40.md rename to C_Universal_Brockchain/markdown/40.md diff --git a/C_Universal_Brockchain/41.md b/C_Universal_Brockchain/markdown/41.md similarity index 100% rename from C_Universal_Brockchain/41.md rename to C_Universal_Brockchain/markdown/41.md diff --git a/C_Universal_Brockchain/42.md b/C_Universal_Brockchain/markdown/42.md similarity index 100% rename from C_Universal_Brockchain/42.md rename to C_Universal_Brockchain/markdown/42.md diff --git a/C_Universal_Brockchain/43.md b/C_Universal_Brockchain/markdown/43.md similarity index 100% rename from C_Universal_Brockchain/43.md rename to C_Universal_Brockchain/markdown/43.md diff --git a/C_Universal_Brockchain/44.md b/C_Universal_Brockchain/markdown/44.md similarity index 100% rename from C_Universal_Brockchain/44.md rename to C_Universal_Brockchain/markdown/44.md diff --git a/C_Universal_Brockchain/45.md b/C_Universal_Brockchain/markdown/45.md similarity index 100% rename from C_Universal_Brockchain/45.md rename to C_Universal_Brockchain/markdown/45.md diff --git a/C_Universal_Brockchain/46.md b/C_Universal_Brockchain/markdown/46.md similarity index 100% rename from C_Universal_Brockchain/46.md rename to C_Universal_Brockchain/markdown/46.md diff --git a/C_Universal_Brockchain/47.md b/C_Universal_Brockchain/markdown/47.md similarity index 100% rename from C_Universal_Brockchain/47.md rename to C_Universal_Brockchain/markdown/47.md diff --git a/C_Universal_Brockchain/48.md b/C_Universal_Brockchain/markdown/48.md similarity index 100% rename from C_Universal_Brockchain/48.md rename to C_Universal_Brockchain/markdown/48.md diff --git a/C_Universal_Brockchain/49.md b/C_Universal_Brockchain/markdown/49.md similarity index 100% rename from C_Universal_Brockchain/49.md rename to C_Universal_Brockchain/markdown/49.md diff --git a/C_Universal_Brockchain/5.md b/C_Universal_Brockchain/markdown/5.md similarity index 100% rename from C_Universal_Brockchain/5.md rename to C_Universal_Brockchain/markdown/5.md diff --git a/C_Universal_Brockchain/50.md b/C_Universal_Brockchain/markdown/50.md similarity index 100% rename from C_Universal_Brockchain/50.md rename to C_Universal_Brockchain/markdown/50.md diff --git a/C_Universal_Brockchain/51.md b/C_Universal_Brockchain/markdown/51.md similarity index 100% rename from C_Universal_Brockchain/51.md rename to C_Universal_Brockchain/markdown/51.md diff --git a/C_Universal_Brockchain/52.md b/C_Universal_Brockchain/markdown/52.md similarity index 100% rename from C_Universal_Brockchain/52.md rename to C_Universal_Brockchain/markdown/52.md diff --git a/C_Universal_Brockchain/53.md b/C_Universal_Brockchain/markdown/53.md similarity index 100% rename from C_Universal_Brockchain/53.md rename to C_Universal_Brockchain/markdown/53.md diff --git a/C_Universal_Brockchain/54.md b/C_Universal_Brockchain/markdown/54.md similarity index 100% rename from C_Universal_Brockchain/54.md rename to C_Universal_Brockchain/markdown/54.md diff --git a/C_Universal_Brockchain/55.md b/C_Universal_Brockchain/markdown/55.md similarity index 100% rename from C_Universal_Brockchain/55.md rename to C_Universal_Brockchain/markdown/55.md diff --git a/C_Universal_Brockchain/56.md b/C_Universal_Brockchain/markdown/56.md similarity index 100% rename from C_Universal_Brockchain/56.md rename to C_Universal_Brockchain/markdown/56.md diff --git a/C_Universal_Brockchain/57.md b/C_Universal_Brockchain/markdown/57.md similarity index 100% rename from C_Universal_Brockchain/57.md rename to C_Universal_Brockchain/markdown/57.md diff --git a/C_Universal_Brockchain/58.md b/C_Universal_Brockchain/markdown/58.md similarity index 100% rename from C_Universal_Brockchain/58.md rename to C_Universal_Brockchain/markdown/58.md diff --git a/C_Universal_Brockchain/59.md b/C_Universal_Brockchain/markdown/59.md similarity index 100% rename from C_Universal_Brockchain/59.md rename to C_Universal_Brockchain/markdown/59.md diff --git a/C_Universal_Brockchain/6.md b/C_Universal_Brockchain/markdown/6.md similarity index 100% rename from C_Universal_Brockchain/6.md rename to C_Universal_Brockchain/markdown/6.md diff --git a/C_Universal_Brockchain/60.md b/C_Universal_Brockchain/markdown/60.md similarity index 100% rename from C_Universal_Brockchain/60.md rename to C_Universal_Brockchain/markdown/60.md diff --git a/C_Universal_Brockchain/61.md b/C_Universal_Brockchain/markdown/61.md similarity index 100% rename from C_Universal_Brockchain/61.md rename to C_Universal_Brockchain/markdown/61.md diff --git a/C_Universal_Brockchain/62.md b/C_Universal_Brockchain/markdown/62.md similarity index 100% rename from C_Universal_Brockchain/62.md rename to C_Universal_Brockchain/markdown/62.md diff --git a/C_Universal_Brockchain/63.md b/C_Universal_Brockchain/markdown/63.md similarity index 100% rename from C_Universal_Brockchain/63.md rename to C_Universal_Brockchain/markdown/63.md diff --git a/C_Universal_Brockchain/64.md b/C_Universal_Brockchain/markdown/64.md similarity index 100% rename from C_Universal_Brockchain/64.md rename to C_Universal_Brockchain/markdown/64.md diff --git a/C_Universal_Brockchain/65.md b/C_Universal_Brockchain/markdown/65.md similarity index 100% rename from C_Universal_Brockchain/65.md rename to C_Universal_Brockchain/markdown/65.md diff --git a/C_Universal_Brockchain/66.md b/C_Universal_Brockchain/markdown/66.md similarity index 100% rename from C_Universal_Brockchain/66.md rename to C_Universal_Brockchain/markdown/66.md diff --git a/C_Universal_Brockchain/67.md b/C_Universal_Brockchain/markdown/67.md similarity index 100% rename from C_Universal_Brockchain/67.md rename to C_Universal_Brockchain/markdown/67.md diff --git a/C_Universal_Brockchain/68.md b/C_Universal_Brockchain/markdown/68.md similarity index 100% rename from C_Universal_Brockchain/68.md rename to C_Universal_Brockchain/markdown/68.md diff --git a/C_Universal_Brockchain/69.md b/C_Universal_Brockchain/markdown/69.md similarity index 100% rename from C_Universal_Brockchain/69.md rename to C_Universal_Brockchain/markdown/69.md diff --git a/C_Universal_Brockchain/7.md b/C_Universal_Brockchain/markdown/7.md similarity index 100% rename from C_Universal_Brockchain/7.md rename to C_Universal_Brockchain/markdown/7.md diff --git a/C_Universal_Brockchain/70.md b/C_Universal_Brockchain/markdown/70.md similarity index 100% rename from C_Universal_Brockchain/70.md rename to C_Universal_Brockchain/markdown/70.md diff --git a/C_Universal_Brockchain/71.md b/C_Universal_Brockchain/markdown/71.md similarity index 100% rename from C_Universal_Brockchain/71.md rename to C_Universal_Brockchain/markdown/71.md diff --git a/C_Universal_Brockchain/72.md b/C_Universal_Brockchain/markdown/72.md similarity index 100% rename from C_Universal_Brockchain/72.md rename to C_Universal_Brockchain/markdown/72.md diff --git a/C_Universal_Brockchain/73.md b/C_Universal_Brockchain/markdown/73.md similarity index 100% rename from C_Universal_Brockchain/73.md rename to C_Universal_Brockchain/markdown/73.md diff --git a/C_Universal_Brockchain/74.md b/C_Universal_Brockchain/markdown/74.md similarity index 100% rename from C_Universal_Brockchain/74.md rename to C_Universal_Brockchain/markdown/74.md diff --git a/C_Universal_Brockchain/75.md b/C_Universal_Brockchain/markdown/75.md similarity index 100% rename from C_Universal_Brockchain/75.md rename to C_Universal_Brockchain/markdown/75.md diff --git a/C_Universal_Brockchain/76.md b/C_Universal_Brockchain/markdown/76.md similarity index 100% rename from C_Universal_Brockchain/76.md rename to C_Universal_Brockchain/markdown/76.md diff --git a/C_Universal_Brockchain/77.md b/C_Universal_Brockchain/markdown/77.md similarity index 100% rename from C_Universal_Brockchain/77.md rename to C_Universal_Brockchain/markdown/77.md diff --git a/C_Universal_Brockchain/78.md b/C_Universal_Brockchain/markdown/78.md similarity index 100% rename from C_Universal_Brockchain/78.md rename to C_Universal_Brockchain/markdown/78.md diff --git a/C_Universal_Brockchain/79.md b/C_Universal_Brockchain/markdown/79.md similarity index 100% rename from C_Universal_Brockchain/79.md rename to C_Universal_Brockchain/markdown/79.md diff --git a/C_Universal_Brockchain/8.md b/C_Universal_Brockchain/markdown/8.md similarity index 100% rename from C_Universal_Brockchain/8.md rename to C_Universal_Brockchain/markdown/8.md diff --git a/C_Universal_Brockchain/80.md b/C_Universal_Brockchain/markdown/80.md similarity index 100% rename from C_Universal_Brockchain/80.md rename to C_Universal_Brockchain/markdown/80.md diff --git a/C_Universal_Brockchain/81.md b/C_Universal_Brockchain/markdown/81.md similarity index 100% rename from C_Universal_Brockchain/81.md rename to C_Universal_Brockchain/markdown/81.md diff --git a/C_Universal_Brockchain/82.md b/C_Universal_Brockchain/markdown/82.md similarity index 100% rename from C_Universal_Brockchain/82.md rename to C_Universal_Brockchain/markdown/82.md diff --git a/C_Universal_Brockchain/83.md b/C_Universal_Brockchain/markdown/83.md similarity index 100% rename from C_Universal_Brockchain/83.md rename to C_Universal_Brockchain/markdown/83.md diff --git a/C_Universal_Brockchain/84.md b/C_Universal_Brockchain/markdown/84.md similarity index 100% rename from C_Universal_Brockchain/84.md rename to C_Universal_Brockchain/markdown/84.md diff --git a/C_Universal_Brockchain/85.md b/C_Universal_Brockchain/markdown/85.md similarity index 100% rename from C_Universal_Brockchain/85.md rename to C_Universal_Brockchain/markdown/85.md diff --git a/C_Universal_Brockchain/86.md b/C_Universal_Brockchain/markdown/86.md similarity index 100% rename from C_Universal_Brockchain/86.md rename to C_Universal_Brockchain/markdown/86.md diff --git a/C_Universal_Brockchain/87.md b/C_Universal_Brockchain/markdown/87.md similarity index 100% rename from C_Universal_Brockchain/87.md rename to C_Universal_Brockchain/markdown/87.md diff --git a/C_Universal_Brockchain/88.md b/C_Universal_Brockchain/markdown/88.md similarity index 100% rename from C_Universal_Brockchain/88.md rename to C_Universal_Brockchain/markdown/88.md diff --git a/C_Universal_Brockchain/89.md b/C_Universal_Brockchain/markdown/89.md similarity index 100% rename from C_Universal_Brockchain/89.md rename to C_Universal_Brockchain/markdown/89.md diff --git a/C_Universal_Brockchain/9.md b/C_Universal_Brockchain/markdown/9.md similarity index 100% rename from C_Universal_Brockchain/9.md rename to C_Universal_Brockchain/markdown/9.md diff --git a/C_Universal_Brockchain/90.md b/C_Universal_Brockchain/markdown/90.md similarity index 100% rename from C_Universal_Brockchain/90.md rename to C_Universal_Brockchain/markdown/90.md diff --git a/C_Universal_Brockchain/91.md b/C_Universal_Brockchain/markdown/91.md similarity index 100% rename from C_Universal_Brockchain/91.md rename to C_Universal_Brockchain/markdown/91.md diff --git a/C_Universal_Brockchain/92.md b/C_Universal_Brockchain/markdown/92.md similarity index 100% rename from C_Universal_Brockchain/92.md rename to C_Universal_Brockchain/markdown/92.md diff --git a/C_Universal_Brockchain/93.md b/C_Universal_Brockchain/markdown/93.md similarity index 100% rename from C_Universal_Brockchain/93.md rename to C_Universal_Brockchain/markdown/93.md diff --git a/C_Universal_Brockchain/94.md b/C_Universal_Brockchain/markdown/94.md similarity index 100% rename from C_Universal_Brockchain/94.md rename to C_Universal_Brockchain/markdown/94.md diff --git a/C_Universal_Brockchain/95.md b/C_Universal_Brockchain/markdown/95.md similarity index 100% rename from C_Universal_Brockchain/95.md rename to C_Universal_Brockchain/markdown/95.md diff --git a/C_Universal_Brockchain/96.md b/C_Universal_Brockchain/markdown/96.md similarity index 100% rename from C_Universal_Brockchain/96.md rename to C_Universal_Brockchain/markdown/96.md diff --git a/C_Universal_Brockchain/97.md b/C_Universal_Brockchain/markdown/97.md similarity index 100% rename from C_Universal_Brockchain/97.md rename to C_Universal_Brockchain/markdown/97.md diff --git a/C_Universal_Brockchain/98.md b/C_Universal_Brockchain/markdown/98.md similarity index 100% rename from C_Universal_Brockchain/98.md rename to C_Universal_Brockchain/markdown/98.md diff --git a/C_Universal_Brockchain/99.md b/C_Universal_Brockchain/markdown/99.md similarity index 100% rename from C_Universal_Brockchain/99.md rename to C_Universal_Brockchain/markdown/99.md diff --git a/README.md b/README.md index ce361e5a..69c36b8c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # 区块链学习指南+笔记(最全) -[TOC] - ---- ##### [Facebook](https://www.facebook.com/profile.php?id=100034435372354) | [Website](https://telsacoin.io/) | [Blog](http://nsddd.top) | [Telegram](https://t.me/smile3293172751) | [Twitter](https://twitter.com/xxw3293172751) | [Linkedin](https://www.linkedin.cn/injobs/in/xiongxinwei-xiong-7606a0227) | [Donate](https://liberapay.com/xiongxinwei/donate) @@ -22,19 +20,21 @@ ### 导航 - [1. Go语言学习](##Golang学习 ) +##### [🖱️参与CUB开发指南](C_Universal_Brockchain/README.md) + + [1. Go语言学习](TOC.md) + [Go语言面试题](https://www.topgoer.cn/docs/gomianshiti/mianshiti) + [Go语言中文官方文档](http://word.topgoer.com/) + [Go语言专家编程地址](http://wen.topgoer.com/docs/gozhuanjia/gogfjhk) - [2. 以太坊学习](##以太坊ETH学习) + [2. 以太坊学习](eth/TOC.md) - [3. 区块链路线](##区块链路线) + [3. 区块链路线](route.md) -+ [区块链公益项目(NFT+私链/联盟链/私链)](区块链公益项目) ++ [区块链公益项目(NFT+私链/联盟链/私链)](区块链公益项目/README.md) + [共识算法以及代码实现(Go)](blockchain/README.md) -+ [区块链技术指南](https://yeasy.gitbook.io/blockchain_guide/01_history) ++ [区块链技术指南](chainbrock-learning/SUMMARY.md) + [区块链实战教程](https://learnblockchain.cn/books/enterprise/) ---- @@ -46,6 +46,8 @@ 😶‍🌫️[go语言官方编程指南](https://golang.org/#) > go语言的官方文档学习笔记很全,推荐可以去官网学习–了解 +> +> 下面介绍的有Go语言路线,Go语言学习笔记,Go语言高级进阶教程,还在更新…… ⏬⏬⏬**理解一个事物最好的方式就是把问题当作机器来层层解析**⏬⏬⏬ @@ -59,11 +61,13 @@ ### 程序员基本素养 -+ [🖱️Go语言包管理工具mod](Gomd_super/mod.md) -+ [🖱️命名规范](Gomd_super/name.md) -+ 🖱️[Go语言目录结构](Gomd_super/catalogue.md) -+ 🖱️[Go文件以及编码处理](Gomd_super/go_file.md) -+ 🖱️[Go-mod包](Gomd_super/go_mod.md) +> 作为一名合格的开发者,必须要会的基本素养 + ++ **[🖱️Go语言包管理工具mod](Gomd_super/mod.md)** ++ **[🖱️命名规范](Gomd_super/name.md)** ++ **🖱️[Go语言目录结构](Gomd_super/catalogue.md)** ++ **🖱️[Go文件以及编码处理](Gomd_super/go_file.md)** ++ **🖱️[Go-mod包](Gomd_super/go_mod.md)** --- @@ -71,19 +75,19 @@ ## 开发必备技术 -### [🖱️ Docker入门到进阶](docker/README.md) - -### [🖱️ Git—必备神器](Git/README.md) +> 参与区块链CUB项目开发必须熟悉Linux指令,熟悉git团队开发,还有是基于docker的容器,更好的迁移开发环境和迁移链码(blockcode) ### [🖱️ linux从入门到精通](linux/README.md) +### [🖱️ Git—必备神器](Git/README.md) +### [🖱️ Docker入门到进阶](docker/README.md) --- ## 区块链学习 -以太坊和比特币一样,底层框架都是区块链协议,区块链本质上是一个应用了密码学技术的分布式数据库系统。建议看一下**以太坊白皮书(需要有golang编程基础)** +> 以太坊和比特币一样,底层框架都是区块链协议,区块链本质上是一个应用了密码学技术的分布式数据库系统。建议看一下**以太坊白皮书(需要有golang编程基础)** @@ -109,8 +113,8 @@ + [区块链开发需要什么](btc/chain.md) -+ [区块链公益项目(NFT+私链/联盟链/私链)](区块链公益项目) -+ [共识算法](blockchain/README.md) ++ [区块链公益项目(NFT+私链/联盟链/私链)](区块链公益项目/README.md) ++ [共识算法——Go语言实现](blockchain/README.md) --- @@ -130,7 +134,7 @@ - +--- ## 如何贡献 diff --git "a/\345\214\272\345\235\227\351\223\276\345\205\254\347\233\212\351\241\271\347\233\256/README.md" "b/\345\214\272\345\235\227\351\223\276\345\205\254\347\233\212\351\241\271\347\233\256/README.md" new file mode 100644 index 00000000..4e1c0ea6 Binary files /dev/null and "b/\345\214\272\345\235\227\351\223\276\345\205\254\347\233\212\351\241\271\347\233\256/README.md" differ