0%

雪崩协议(Avalanche)是一个新型的共识协议,它继承了Non-Byzanting和Nakamoto共识算法两者的优点,具备了简单又高效的特点。

背景

BFT和PoW算法

拜占庭容错算法BFT是由两位伟大的计算机科学家Leslie Lamport和Barbara Liskov在1982年共同开发的。BFT算法的优点是可以快速达成共识,但其也有缺点:

  • 扩展性差:无法扩展到1000个节点以上,因为它们通信成本正比于节点个数的二次方,即O(n^2)
  • 要求网络中的每个人都知道所有其他参与者。

因此BFT比较适合在联盟链场景下使用,而对于缺乏信任的公有链场景,BFT算法则不适用。

最早在公链场景下使用的共识算法是PoW算法,它具备不需要知道网络中参与者和可扩展性强的特点,相对的它也缺点也很明显:

  • 交易确认比较慢。平均而言,用户必须等待大约10–60分钟才能确认他们的交易被存储在链上。
  • 吞吐量较低。比特币每秒可处理的交易大约是3-7笔。
  • 能耗较高。PoW的运算消耗了大量电力。

共识协议族 Consensus Family

Emin教授在深入研究前述两种主流共识机制后,受 Gossip 协议的启发,提出了一组BFT协议(简称“共识家族”,Consensus family)。共识协议族有四种协议组成:

  • Slush 协议:是共识协议族的第一个协议,它是一个非拜占庭协议(Non-byzanting protocol,后续三个协议都是 BFT 协议)。Slush协议的特点是:简单状态(simple state),小样本(small sample),反复抽样(repeated sampling),抽样轮数或时间期限(用 m 表示),Slush 协议作为 BFT 协议的原始状态,不能提供完整的 BFT 保证。
  • Snowflake 协议:也叫 “BFT Snowflake”,它是共识协议族的第二个协议,在 Slush 的基础上扩展而来。Snowflake 为每个节点增加一个 counter, 用来记录一个节点当前 color 的可信度。Snowflake 可以保证对最小的状态做出很强的保证。
  • Snowball 协议:Snowball 是共识协议族中的第三个协议,它对 Snowflake 协议做了改进,添加一个更持久的可信度标志,使得协议安全性更高,进一步增加了共识结果的可靠性(confidence)。Snowball 不仅比 Snowflake 更难攻击,而且协议更加通用化了。
  • Avalanche 协议(DAG):Avalanche 是共识协议族中的第四个协议,也是最核心协议,它在 Snowball 的基础上添加一个动态的仅限追加(append-only)DAG 结构来记录所有的交易。DAG 结构给雪崩协议Avalanche带来了两大优势:高效,安全。

亚稳态模型 Metastable Mechenism

共识协议族中的协议全部基于亚稳态模型,共识协议中最糟糕的事情是无法在两个选择之间做出决定,即你不希望它模糊地说两个事件中有一个发生了,实际你期望它能确定两个事件中到底哪个发生了哪个没发生。

雪崩协议的核心思想依赖于随机抽样。该协议旨在于倾向某一结果而不是留在模棱两可的中间选项。随着它的倾向性越来越高,网络的感知的颜色将会转移到其中一种颜色。我们向一个结果方向移动的速度(节点对一种颜色投票比另一种颜色更多)将会不断加快,并且在某个时候我们达到不返回点,整个网络已就颜色达成一致。

雪崩协议特点

雪崩协议继承了Non-Byzanting和PoW协议两者的优点:

  • 快速结算和低延迟:在全球范围内实现结算需要大约2-4秒钟
  • 更高的吞吐量:每秒可处理1000–10,000个交易。
  • 稳健:网络无需就参与者是谁达成一致。
  • 静态协议:最重要的是,协议是绿色的。这意味着它是可持续的,它不会浪费任何能量,并且没有特殊的矿工生态系统,其中矿工的利益与用户的利益无关。

另外,雪崩协议还有以下特点:

  • 高效的可扩展性:协议是轻量级的,因此提供可扩展性和低延迟。
  • 拜占攻击庭容忍度:它可以容忍大量的拜占庭参与者,而不对安全性产生影响。特别地,它可以容忍多达50%的节点作为拜占庭节点(即尝试欺骗网络并保持整个网络不平衡的节点)。但是,它们无法让两个节点决定两种不同颜色
  • 平等主义生态系统:雪崩协议产生了一个平等主义的生态系统,即网络中的所有节点都是相同的。 没有矿工,也没有特权。
  • 冲突交易不受保护:如果攻击者试图在两次不同的交易中花费相同的钱两次(双重支出),那么雪崩协议将无法在这两种交易之间做出选择,导致这笔钱丢失。经典共识和中本聪协议将会选择其中一个交易,但是雪崩协议不会。这是雪崩协议的一个非常有趣的属性,它会隐式而又自然地惩罚坏的角色,而协议本身没有增加任何额外的复杂读。

Avalanche是一个开源的平台,旨在为部署去中心化应用和企业级区块链提供可互操作、高扩展型的生态系统。Avalanche可以达到4500+的TPS以及交易即时确认。另外,以太坊的开发者可以快速的在Avalance创建应用,因为Solidity合约开箱即用。与其他分布式网络的区别是,Avalance采用了一种新颖的共识协议,来实现强安全性、快速确认和高吞吐率,同时也不会损失去中心化特性。

基本信息

Tornado.Cash是一个基于zkSNARKs的以太坊的隐私交易协议,通过切断交易的源地址和目的地址的关联来实现交易的隐私。Tornado使用智能合约接受E TH存款,而存款可以有另一个地址进行取款,甚至可以是一个取到一个无以太坊balance的地址。无论ETH是否是取回到新的地址,都无法关联源地址和目的地址,从而实现了隐私。

基本信息

Tornado.Cash基本流程

Tornado.Cash的基本流程大概分为三步:Deposit、Wait、Withdraw

  1. Deposit

    用户生成一个secret,并把其Hash(也称为commitment)以及交易的数量发送给Tornado智能合约。

  2. Wait

    用户提交Deposit之后需要等待Tornado合约进行处理,即Tornado合约接受deposit请求,并把commitment添加到列表。

  3. Withdraw

    当用户需要取款时,需要提供Proof来证明他拥有secret,关联到合约deposit列表中的某个未花费的commitment。而零知识证明协议zkSNARKs保证了这个过程并不会泄漏secret关联了哪个deposit。Tornado智能合约会校验proof,并转移deposit的资金到取款地址。这样外部的观察者并不会知道这笔存款来自哪个地址。

2006年Google发表了BigTable论文,推动了LSM存储架构的流行。LSM(Log Structured Merge-Tree)是一种数据组织形式,大多数的NoSQL都是基于LSM架构实现的。由于区块链系统基本上使用像Level DB之类的NoSQL数据库,因此,本文将对LSM的概念、原理和操作进行介绍。

背景

传统关系型数据库采用B树或者B+树作为数据存储结构,从而进行高效查找。但这种存储模式保存着磁盘时会有一个明显的缺陷,即逻辑上很近的块在物理磁盘可能很远,这就会产生大量的随机读写。对磁盘来说,随机读写比顺序读写速度慢的多,为了提高性能,需要将对磁盘的随机操作转换为顺序操作。而LSM的产生就是为了这个目的,将随机写转化为顺序写,从而大幅提高写性能,但也牺牲了一些读性能。

LSM原理

LSM的实现原理大致如下:

  1. 当有写操作时,写入位于内存的buffer,内存中通过某种数据结构(如红黑树、map、skiplist)保持key有序;一般实现也会将数据追加写到磁盘Log文件,以备必要时恢复。
  2. 内存中的数据定时或按固定大小地刷到磁盘文件(sstable)。更新操作只不断地写到内存,并不更新磁盘上已有的文件。
  3. 随着数据越写越多,磁盘文件也越写越多,这些文件变的不可写且有序。
  4. 定时对文件进行compaction操作,消除冗余数据,减少文件数量。

LSM原理图

从上面的实现原理可以看出,LSM的写操作只更新内存,而内存中的数据以数据块的形式刷到磁盘,这个过程是顺序磁盘IO操作;另外,磁盘文件也会定期进行compaction操作,这个过程也涉及磁盘IO操作。

LSM的读操作先从内存进行访问,如果内存访问不到,再逆序从磁盘文件中进行查找。由于磁盘文件本身是有序的,且定期的compaction操作减少了文件数量,因此查找过程相对较快。

LSM Compaction

Compaction是LSM实现中重要的一步。写数据时由于较旧的文件并不会被更新,重复的记录只会通过创建新的记录来覆盖,因此会产生很多冗余数据,而Compaction操作就是用来消除这些冗余数据,同时也能减少文件数量。

按文件大小的Compaction:当一定数量的sstable文件被创建,例如有5个sstable,每一个有10行,他们被合并为一个50行的文件(或者更少的行数)。这个过程一 直持续着,当更多的有10行的sstable文件被创建,当产生5个文件时,它们就被合并到50行的文件。最终会有5个50行的文件,这时会将这5个50行的文件合并成一个250行的文件。这个过程不停的创建更大的文件。这种方式有个问题就是大量的文件被创建,在最坏的情况下所有文件都要被搜索。

Levelled Compaction

Levelled Compaction:而Level DB、Cassandra等NoSQL中,使用了基于层级的Compaction方案(Levelled Compaction),而不是根据固定文件大小进行Compaction,即生成第N层时,采用第N-1层的数据进行排序合并,使得每层的数据文件都是有序的。另外,每一层可以维护指定的文件个数,同时保证不让key重叠,也就是说把key分区到不同的文件,因此在一层查找一个key,只用查找一个文件。但在第一层,由于不断有数据文件产生,只能保证文件内的数据有序。

读优化

当一个读操作请求时,系统首先检查内存数据(memtable),如果没有找到这个key,就会逆序的一个一个检查sstable文件,直到key 被找到。因为每个sstable都是有序的,所以查找比较高效(O(logN)),但是读操作会变的越来越慢随着sstable的个数增加,因为每一个 sstable都要被检查。假如K为sstable个数, N 为sstable平均大小,查找的复杂度是O(K log N)。因此,LSM的读操作比采用本地更新的结构要慢。

页缓存:可以采用一些技巧提高读性能,最基本的方法是页缓存到内存中,以减少二分查找的时间。对LevelDB来说即TableCache,即将sstable按照LRU算法缓存到内存中。LevelDB和BigTable是将 block-index 保存在文件尾部,这样查找就只要一次IO操作,如果block-index在内存中。一些其它的系统则实现了更复杂的索引方法。

布隆过滤器:即使有每个文件的索引,随着文件个数增多,读操作仍然很慢。通过周期的合并文件,来保持文件的个数,因些读操作的性能在可接收的范围内。即便有了合并操作,读操作仍然会访问大量的文件,大部分的实现通过布隆过滤器来避免大量的读文件操作,布隆过滤器是一种高效的方法来判断一个sstable中是否包含一个特定的key。

BloomFilter读

总结

LSM通过管理一组索引文件,而不是单一的索引文件,将随机IO转换为顺序IO,从而提高了写性能,而代价是读操作需要处理大量的索引文件。

LSM的原理与区块链的块数据和状态数据处理模式很相似,因此区块链很适合使用LSM实现的NoSQL数据库(如LevelDB)进行数据存储。

参考

  1. 看图轻松理解数据结构与算法系列(NoSQL存储-LSM树)
  2. LSM Tree存储组织结构介绍
  3. Log Structured Merge Trees(LSM) 原理

本文 将对DeFi的概念、优势、应用场景、面临的挑战等方面进行介绍,并对DeFi和开放银行进行了对比。

什么是DeFi

DeFi是去中心化金融(DeCentralized Finance)的缩写,指在区块链网络上构建的金融应用生态。更具体的来说,DeFi旨在建立开源、公开、透明的去中心化金融应用生态,没有中心化机构运作且可以对所有人提供服务。用户能完全控制自己的资产,使用Dapp通过P2P网络与DeFi应用进行交互。

DeFi的核心优势是用户可以很容易获取金融服务,尤其是那些脱离与现代金融系统之外的用户。DeFi另一个潜在的优势是其模块化框架,通过DeFi应用的交互可能会创建完全新型的金融市场、产品和服务。

DeFi的主要优势

不依赖机构

传统金融依赖中心化机构,如银行作为中介机构、法院作为仲裁机构等。而DeFi应用则无需依赖任何中介机构或仲裁机构,而是通过代码来解决任何可能的争议,资产则完全由用户掌控。这将会降低金融成本,并能建立一套更顺畅的金融系统。

避免单点故障

由于DeFi建立在区块链之上,这将会消除单点故障。数据记录在区块链上并传播到成千上万个节点,那么服务的审查和潜在的崩溃则 变得十分困难。另外,由于DeFi应用框架都是预先建立的,那么构建一个DeFi应用会更容易更安全。

更容易提供服务

DeFi可以更容易为那些无法获取传统金融服务的人提供金融服务。由于传统金融依赖于追求利润的中介结构,低收入国家和地区的人则很难获取金融服务。而DeFi大幅降低了成本,低收入人群也可以从丰富的金融服务中受益。

DeFi的潜在应用场景

借贷 Borrowing & Lending

公开的贷款协议是DeFi系统中最流行的应用之一。与传统借贷相比,DeFi借贷有很多优势,包括实时结算、数字资产抵押、无需信用审查、以及未来的标准化。

由于借贷服务是在区块链上构建的,因此可以通过密码学验证来降低信任成本。区块链上的借贷市场可以降低对手方的风险,使借贷服务更便宜、快速,并能服务与更多人。

银行货币服务 Monetary banking services

从定义上来说,DeFi是金融应用,那么铸币服务也是一个比较明显的应用场景,包括稳定币的发行、抵押贷款和保险。

随着区块链技术的成熟,稳定币的出现获得了越来越多的关注。稳定币是一种锚定了真实世界资产的加密货币,但能够以数字的形式实现更容易的转移。由于加密货币价格是随时波动的,去中心化的稳定币则可以作为电子现金用于日常支付,而且这种电子现金并不由中心机构发行和监控。

由于抵押贷款需要多个中介机构参与,因此其流程比较昂贵和耗时。而DeFi使用了智能合约,则承销和合法费用则被大幅度降低。

链上保险可以去掉中介结构并在众多参与者之间分散风险,从而在保证服务质量的情况下有效降低保费。

去中心化市场 Decentralized Marketplaces

评估去中心化市场的潜力是具有挑战性的,它是能为金融服务带来巨大的创新空间。

颇有争议的是,DeFi最重要的应用是去中心化交易所(Decentralized Exchanges, DEXes)。这些平台可以使用户交易数字资产,而不需要中心化交易所托管他们的资产。用户可以在智能合约的帮助下直接通过钱包进行交易。

因为去中心化交易几乎不需要维护,因此与中心化交易所相比手续费更低,并且能去掉保管风险和单点故障。例如,代币安全发行平台可以为发行者提供工具和资源使用自定义的参数保证代币化安全。

另外一些项目可以提供衍生品、组合资产、去中心化市场指数等金融服务。

智能合约在DeFi中的作用

大部分DeFi应用都设计智能合约的创建和执行。与法律合约使用法律条文约束合约实体间的关系,智能合约则使用计算机代码。由于智能合约通过代码实现规则,那么它可以使用代码保证规则的执行,以运行目前需要人工干预的大量商业逻辑的可信和自动化执行。

使用智能合约具备更快速、更方便、降低风险的特点。但另一方面,智能合约也引入了另一种风险,即智能合约可能出现bug,使锁定在合约中的资产遭受风险。

DeFi面临的挑战

DeFi面临以下几个挑战:

  • 性能较差:与中心化系统相比,区块链和区块链应用的性能较低,因此需要DeFi的开发者对账户进行限制并对他们的产品进行优化。
  • 用户错误的风险:由于DeFi将中介的职责转移给了用户,用户的错误则会造成高风险。由于DeFi产品部署在不可篡改的区块链上,设计最小化用户错误风险的产品则是十分具有挑战性的。
  • 用户体验差:目前,使用DeFi应用需要用户侧进行额外的工作量。因此DeFi应用必须向用户体现明显的优势,从而激励用户从传统金融切换到DeFi。
  • 生态较混乱:针对应用场景选择合适的DeFi产品是很困难的,因此用户需要具备寻找最佳选择的能力。因此,目前面临的挑战不仅是建立DeFi应用,在广阔的生态中提供合适的产品也是十分重要的。

DeFi和开放银行的区别

开放银行是通过API向第三方金融机构提供金融数据的安全访问的银行系统。开放银行需要银行和非银行机构交互,从本质上来说,开放银行是传统金融体系中的一种新的形式。

而DeFi是与现在的金融系统独立的全新的金融系统,DeFi有时候也被称为开放金融。例如,开放银行可以在一个应用上通过从多个银行和机构获取数据来管理所有传统的金融设施。而DeFi可以管理新的金融设施以及用全新的方式与其进行交互。

总结

DeFi致力于构建一个与传统金融独立的金融体系,以提供更开放的金融服务,来解决传统金融审查过严和歧视服务的问题。但去中心化不是万能的,寻找与区块链的特点匹配的应用场景是构建有效的金融产品的关键。如果DeFi是成功的,它将会把更多权利从中心化机构让渡给开源社区和金融用户。一旦DeFi被大众所接受,更有效率的金融系统也就好建立。

背景

对于Fabric来说,如果一个Peer想要处理交易,那么它至少需要拥有背书和验证交易的最小集的账本数据。而这些数据包含“world state”(世界状态or全局状态),即最近一个区块提交后账本中所有Key-value集合。目前Fabric有两种方式供一个新加入的peer同步状态数据:

  1. 从区块构建全局状态。从创世区块(genesis block)开始,依次从排序服务节点拉取区块,并在本地对区块进行处理,一直到最新的区块。
  2. 从快照(Snapshot)构建全局状态。快照包含了到特定区块为止最小集的账本数据,因此加入通道无需拉取和处理全部的区块。

在v2.3.0之前,Fabric只支持从区块构建全局状态,在2020年11月发布的Fabric v2.3.0加入了对快照的支持。第一种方法来说,如果通道的规模较大(例如通道已经包含了数千个区块),那么从创世区块开始处理所有已经提交的区块则是一个非常耗时的过程。另外存储从创世区块开始的所有区块对存储也是一种压力。与第一种方法相比,通过快照加入通道仅需提供最新的配置区块(configuration block)。如果在创世区块之后,通道的配置发生过改变,则通过快照加入通道是一个很有优势的方法。

使用快照的一些考量

  • 当一个peer加入通道时,是选择通过区块同步加入还是通过快照加入,需要考量从创世区块开始通道中区块的数量,从而影响加入通道的时间;需要考量peer是否能够基于创世区块原始的配置而从排序服务拉取区块;需要考虑是否需要查询通道的历史(历史区块、历史交易和历史状态);
  • 如果你的背书请求或者查询不需要最新区块提交,那么就可以使用生成快照的peer;
  • 如果你的背书请求或着查询需要最新区块提交,那么可以利用discovery服务来识别和定位通道中拥有最高区块的peer,从而避免正在生成快照的其他peer。另外,你也可以设置专门的生成快照的节点,且不可用于背书和查询功能,例如不设置peer的peer.gossip.external从而让peer不参与discovery服务;

使用快照的限制

  • 使用快照加入通道的节点无法查询低于生成快照高度的区块;同理,也不能查询状态数据早于快照的历史。例如peer加入通道使用的快照是在第1000块上生成的,那么此peer则不能查询0-999号区块以及相关的状态历史。因此,建议每个机构至少有一个节点能够包含所有的历史数据,以用于查询历史。
  • 正在生成快照的peer无法提交区块。因为生成快照是一个资源消耗大的操作,因此可能会影响背书及查询的效率。基于这个原因,尽量在必要时才进行生成快照的操作,如当有新节点要加入通道的时候。
  • 由于通道中的各个机构的私有数据可能不同,因此快照并不会包含私有数据本身(可能包含私有数据的哈希)。当peer通过快照加入通道后,它会直接从同为私有数据成员的节点处发现和获取私有数据集合,然后进行私有数据校验
  • 生成快照不会对peer的账本进行归档和删除,并且快照不能当成peer全量备份的方法,因为私有数据和配置信息不会包含在快照里。
  • 对于通过快照加入通道的节点,无法使用reset, roolback, rebuild-dbs命令,因为节点不包含所有的区块文件。

使用快照

使用快照功能可以分为两个部分,一是生成快照,二是通过快照加入通道。

生成快照

生成快照的过程可分为三个步骤:

  1. 预定(schedule)快照生成。由于某个高度快照的生成要求peer必须恰好在此高度,所以快照的高度必须等于或大于账本当前的高度。另外使用快照加入通道的peer也可以用来生成快照。快照可以按需进行生成或者按照一定的规律去生成,比如每1000个区块生成一次快照。当使用快照加入通道时,建议使用离最新的通道配置区块较近的快照,因为这样可以确保peer使用包括最新排序服务endpoints及CA证书的通道配置。
  2. 当账本高度到达预设快照生成高度,快照就会被peer生成。生成的快照是一个目录,包含了公开状态、私有状态的哈希、交易ID、collection配置历史等文件。另外,还包括一个包含以上文件的元信息的文件。
  3. 如果快照被新的机构使用,需要把快照发送给此机构。这个过程是在线下进行的,因为快照文件是非压缩的,peer管理员可能需要在发送快照前进行压缩。在一般的场景中,可以从一个机构获取快照,但从另一个机构获取快照元信息文件来校验快照。

使用快照加入通道

使用快照加入通道也分为三个步骤:

  1. 校验快照。当peer管理员使用快照加入通道前,需计算快照的哈希,然后与元信息文件上的哈希进行比对。另外,基于不同的信任模型,peer管理员可能需要从不同的机构获取元信息文件。不仅如此,peer管理员可能会要求提供快照的机构进行签名。
  2. 使用快照加入通道。当peer使用快照加入通道后,就开始拉取它属于的私有数据集。而且也开始像正常peer一样提交区块。
  3. 校验peer加入通道成功。可以通过获取通道信息来校验是否加入通道成功。

快照文件内容

快照文件生成在 {ledger.snapshots.rootDir}/completed/{channelName}/{lastBlockNumberInSnapshot} 目录,而 ledger.snapshots.rootDir 可以通过peer的yaml配置文件映射到自定义的目录。

快照目录包含了如下内容:

  • Public state:通道中所有最新的key-value值。
  • Private data hashes:通道私有数据交易的哈希。
  • Transactions IDs:通道使用的所有交易的ID。
  • Collection config history:对于所有chaincode的collection配置的历史。

另外,开展还包含了两个元信息文件,以对快照数据进行校验。对于某个高度,快照的元信息文件都是相同的。

一个文件是名为 _snapshot_signable_metadata.json 的元信息文件,包含以下内容:

  • channel_name:通道的名字
  • last_block_number:生成快照的区块高度
  • last_block_hash:生成快照的区块哈希
  • previous_block_hash:生成快照的区块的前一个区块
  • state_db_type:状态数据库的类型,CouchDB或者LevelDB
  • snapshot_files_raw_hash:是一个json记录,包含了上面所述的快照文件的哈希。

另一个文件也是json记录,包含两个数据域:

  • snapshot_hash: _snapshot_signable_metadata.json 文件的哈希,这个哈希也被当成是快照的哈希
  • lash_block_commit_hash: 记录了生成快照的peer是否有能力计算区块提交的哈希。

具体操作

快照涉及的操作也包括两个部分,一是生成快照,二是使用快照加入通道。

生成快照

  • peer channel getinfo 获取当前账本高度

在生成快照前,最好先确认当前账本的高度:

1
peer channel getinfo -c <name of channel>

命令执行成功可以看到类似于下面的内容:

1
Blockchain info: {"height":970,"currentBlockHash":"JgK9lcaPUNmFb5Mp1qe1SVMsx3o/22Ct4+n5tejcXCw=","previousBlockHash":"f8lZXoAn3gF86zrFq7L1DzW2aKuabH9Ow6SIE5Y04a4="}

在例子中,当前账本的高度是970,那么你可以生成等于或高于970的快照。

  • peer snapshot submitrequest 提交快照生成

生成快照的命令如下:

1
peer snapshot submitrequest -c <name of channel> -b <ledger height where snapshot will be taken> --peerAddress <address of peer> --tlsRootCertFile <path to root certificate of the TLS CA>

例如:

1
peer snapshot submitrequest -c testchannel -b 1000 --peerAddress 127.0.0.1:22509 --tlsRootCertFile tls/cert.pem

如果将 设置为0,那么快照就会立即生成。这个功能可以在组织内部生成快照且不会发送给其他组织时使用,因为如果跨机构使用会证件不同快照高度的相似性。

  • peer snapshot listpending 查看快照生成请求

示例命令如下:

1
peer snapshot listpending -c testchannel --peerAddress 127.0.0.1:22509 --tlsRootCertFile tls/cert.pem

如果执行成功,会显示如下信息:

1
Successfully got pending snapshot requests [1000]
  • peer snapshot cancelrequest 删除快照生成请求

示例命令如下:

1
peer snapshot cancelrequest -c testchannel -b 1000 --peerAddress 127.0.0.1:22509 --tlsRootCertFile tls/cert.pem

使用快照加入通道

使用快照加入通道的命令如下:

1
peer channel joinbysnapshot --snapshotpath <path to snapshot>

OpenZeppelin提供安全产品来构建、自动化部署、操作去中心化应用,同时也为头部机构的系统和产品提供安全审计服务。Openzeppelin-contracts是包含安全智能合约开发的库的仓库。Openzeppelin-contracts提供了以下功能:

  • ERC20和ERC721标准的实现
  • 弹性的基于role的权限机制
  • 提供可复用的Solidity组件,来构建自定义的合约及复杂的去中心化系统
  • 为系统提供Gas Station Network的最优继承,且无需gas费用
  • 头部安全公司的审计

A library for secure smart contract development. Build on a solid foundation of community-vetted code.

  • Implementations of standards like ERC20 and ERC721.
  • Flexible role-based permissioning scheme.
  • Reusable Solidity components to build custom contracts and complex decentralized systems.
  • First-class integration with the Gas Station Network for systems with no gas fees!
  • Audited by leading security firms (last full audit on v2.0.0).

具体功能可参考Contract文档。以下所列的合约并不是OpenZeppelin library的一部分。

参考:list-of-solidity-libraries-in-the-wild

Math

Integer 整型运算

  • math.sol, by DappHub. Mixin for inline numerical wizardry.
  • CarefulMath.sol, by Compound.
  • Math.sol, by alianse777.
  • SafeMathInt256, by Augur 1. Int256 math operations with safety checks that throw on error.
  • SafeMathUint256.sol, by Augur 1. Uint256 math operations with safety checks that throw on error.

Floating Point 浮点型运算

  • Multiprecision.sol, by alianse777.
  • ABDKMathQuad.sol, by ABDK. Smart contract library of mathematical functions operating with IEEE 754 quadruple-precision binary floating-point numbers (quadruple precision numbers). :warning: Weird license.

Fixed Point 定点小数运算

  • FixidityLib.sol, by CementDAO. This library provides fixed point arithmetic with protection against overflow.
  • SafeDecimalMath.sol, by Synthetix. Safely manipulate unsigned fixed-point decimals at a given precision level.
  • Exponential.sol, by Compound. Exponential module for storing fixed-precision decimals.
  • ABDKMath64x64, by ABDK. Smart contract library of mathematical functions operating with signed 64.64-bit fixed point numbers. :warning: Weird license.
  • FixedPoint.sol, by UMA . Library for fixed point arithmetic on uints with 18 digit precision.
  • RealMath.sol, by Macroverse.
  • BNum.sol, by Balancer. Library for fixed point arithmetic with 18 digit precision.

Fractions 有理数

  • FractionUtil.sol, by Celo.

Random 随机

  • UniformRandomNumber.sol, by PoolTogether. A library that eliminates modulo bias when bounding random numbers to a smaller range.
  • Random.sol, by alianse777. “random” numbers generator (not cryptographicaly secure).

Bytes 字节数组

  • BytesLib.sol, by Gonçalo Sá . Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
  • LibBytes.sol, by Augur .

Arrays 数组

  • Array.sol, by alianse777.

Type conversion 类型转换

  • UintToString.sol, by Augur. Uint operations to convert to string.
  • BytesToString.sol, by Augur. Bytes operations to convert to string and remove null characters.
  • Convert.sol, by alianse777.

Memory 内存

  • Memory.sol, by alianse777.

Proxy 代理

  • MinimalProxy.sol, by Ripio.
  • ProxyFactory.sol, by GNOSIS.

Wallet 钱包

MultiSig 多签

  • MultiSigWallet.sol, by GNOSIS. Allows multiple parties to agree on transactions before execution.
  • MultiSigWalletWithTimeLock.sol, by 0x. Multisignature wallet with time lock- Allows multiple parties to execute a transaction after a time lock has passed.

Data Structures 数据结构

  • LibCLL.sol, by o0ragman0o. A Solidity library for implementing a data indexing regime using a circular linked list.
  • StructuredLinkedList.sol, by Vittorio Minacori.
  • LinkedList.sol, by TechHQ.
  • DoubleLinkedList.sol, by TechHQ.
  • StateMachine.sol, by TechHQ.
  • SortitionSumTreeFactory, by Kleros. Allows values to be proportionally weighted with O(log(n)) efficiency. The linked package is actually the Kleros -codebase; ideally it should be extracted into its own library.
  • Heap.sol, by Celo. Simple heap implementation.
  • LinkedHashMap.sol, by AKA protocol.
  • HitchensOrderStatisticsTree.sol, by Rob Hitchens. A Solidity Red-Black Tree library to store and maintain a sorted data structure in a Red-Black binary search tree, with O(log 2n) insert, remove and search time (and gas, approximately).

Strings 字符串

  • strings.sol, by Arachnid. String & slice utility library for Solidity contracts.

Dates 日期

  • BokkyPooBahsDateTimeLibrary.sol, by bokkypoobah. A gas-efficient Solidity date and time library.

Serialization 序列号

  • Base64.sol, by OpenZeppelin.
  • JsmnSolLib.sol, by chrisdotn.

Encoding 编码

  • RLPEncode.sol, by OMG Network.

Cryptography 加解密

  • TypedSignature.sol, by dYdX. Library to unparse typed signatures.
  • SolRsaVerify.sol, by adria0. Verification of RSA Sha256 Pkcs1.5 Signatures.

Tokens 代币

  • ERC20DividendableEth.sol, by TechHQ.

Voting 投票

  • Voting.sol, by TechHQ.

时间锁

  • Timelock.sol, by Compound.

开发环境部署

环境准备

  • 安装openssl和curl

开发部署工具 build_chain.sh脚本依赖于openssl、curl,因此需要首先安装openssl和curl。对于CentOS,使用yum安装;对于MacOS,使用brew安装。

1
2
3
4
// CentOS
yum install -y openssl curl
// MacOS
brew install -y openssl curl
  • 创建目录
1
cd ~ && mkdir -p fisco && cd fisco
  • 下载build_chain.sh工具
1
curl -LO https://github.com/FISCO-BCOS/FISCO-BCOS/releases/download/v2.4.0/build_chain.sh && chmod u+x build_chain.sh

如果因网络问题长时间未下载完成,可以使用如下链接下载:

1
curl -LO https://gitee.com/FISCO-BCOS/FISCO-BCOS/raw/master/tools/build_chain.sh && chmod u+x build_chain.sh

搭建单机四节点集群

  • 生成四节点的配置

在fisco目录下执行下面的指令,生成一条单群组4节点的FISCO链。 请确保机器的30300 ~ 30303,20200 ~ 20203,8545 ~ 8548端口没有被占用

1
bash build_chain.sh -l "127.0.0.1:4" -p 30300,20200,8545

注解:
其中-p选项指定起始端口,分别是p2p_port,channel_port,jsonrpc_port

  • 启动单机四节点集群

启动所有节点

1
bash nodes/127.0.0.1/start_all.sh
  • 检查进程是否成功
1
ps -ef | grep -v grep | grep fisco-bcos

使用控制台

环境准备

  • Java环境配置

参考Java环境配置

  • 获取控制台
1
cd ~/fisco && curl -LO https://github.com/FISCO-BCOS/console/releases/download/v1.0.9/download_console.sh && bash download_console.sh

如果因网络问题长时间未下载完成,可以使用如下链接下载:

1
cd ~/fisco && curl -LO https://gitee.com/FISCO-BCOS/console/raw/master/tools/download_console.sh
  • 拷贝控制台配置文件
1
cp -n console/conf/config-example.toml console/conf/config.toml
  • 配置控制台证书
1
cp nodes/127.0.0.1/sdk/* console/conf/

启动控制台

  • 启动
1
cd ~/fisco/console && bash start.sh

使用控制台获取信息

1
2
3
4
5
6
# 获取帮助信息
[group:1]> help
# 获取客户端版本
[group:1]> getNodeVersion
# 获取节点链接信息
[group:1]> getPeers

部署及调用HelloWorld合约

  • HelloWorld合约
1
2
3
4
5
6
7
8
9
10
11
12
13
pragma solidity>=0.4.24 <0.6.11;
contract HelloWorld {
string name;
constructor() public {
name = "Hello, World!";
}
function get() public view returns (string memory) {
return name;
}
function set(string memory n) public {
name = n;
}
}
  • 部署HelloWorld合约

HelloWorld合约已经内置于控制台中,位于控制台目录下contracts/solidity/HelloWorld.sol

1
2
3
[group:1]> deploy HelloWorld
transaction hash: 0x40413207aa2ab3b4fb1b9911c0e735b8a4e1a0dd3af58202169a42a06d9a9176
contract address: 0x673c7db07e818df412f2b8fbfb2c96ccdee1cbfd
  • 调用HelloWorld合约
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 查看当前块高
[group:1]> getBlockNumber
1

# 调用get接口获取name变量 此处的合约地址是deploy指令返回的地址
[group:1]> call HelloWorld 0xb3c223fc0bf6646959f254ac4e4a7e355b50a344 get
Hello, World!

# 查看当前块高,块高不变,因为get接口不更改账本状态
[group:1]> getBlockNumber
1

# 调用set设置name
[group:1]> call HelloWorld 0xb3c223fc0bf6646959f254ac4e4a7e355b50a344 set "Hello, FISCO BCOS"
0x21dca087cb3e44f44f9b882071ec6ecfcb500361cad36a52d39900ea359d0895

# 再次查看当前块高,块高增加表示已出块,账本状态已更改
[group:1]> getBlockNumber
2

# 调用get接口获取name变量,检查设置是否生效
[group:1]> call HelloWorld 0xb3c223fc0bf6646959f254ac4e4a7e355b50a344 get
Hello, FISCO BCOS

# 退出控制台
[group:1]> quit

SDK调用测试

合约编译

由于Java程序无法直接调用solidity合约,因此需要将solidity合约编译成java文件。控制台提供了sol2java.sh脚本。

1
2
3
4
# 切换到fisco/console/目录
$ cd ~/fisco/console/
# 编译合约,后面指定一个Java的包名参数,可以根据实际项目路径指定包名
$ ./sol2java.sh org.fisco.bcos.asset.contract

注解:
sol2java.sh脚本使用方法:

1
./sol2java.sh [packageName] [solidityFilePath] [javaCodeOutputDir]

其中,$[packageName]$表示生成的java文件的包名;$[solidityFilePath]$表示solidity源文件的路径,默认为$contracts/solidity$;$[javaCodeOutputDir]$表示生成的java文件的路径,默认为$contracts/sdk/java$

SDK配置

  • 下载Java样例工程项目
1
2
curl -LO https://github.com/FISCO-BCOS/LargeFiles/raw/master/tools/asset-app.tar.gz
tar -zxf asset-app.tar.gz
  • 目录结构

asset-app的目录结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

|-- build.gradle // gradle配置文件
|-- gradle
| |-- wrapper
| |-- gradle-wrapper.jar // 用于下载Gradle的相关代码实现
| |-- gradle-wrapper.properties // wrapper所使用的配置信息,比如gradle的版本等信息
|-- gradlew // Linux或者Unix下用于执行wrapper命令的Shell脚本
|-- gradlew.bat // Windows下用于执行wrapper命令的批处理脚本
|-- src
| |-- main
| | |-- java
| | |-- org
| | |-- fisco
| | |-- bcos
| | |-- asset
| | |-- client // 放置客户端调用类
| | |-- AssetClient.java
| | |-- contract // 放置Java合约类
| | |-- Asset.java
| |-- test| |-- resources // 存放代码资源文件
| |-- applicationContext.xml // 项目配置文件
| |-- contract.properties // 存储部署合约地址的文件
| |-- log4j.properties // 日志配置文件
| |-- contract //存放solidity约文件
| |-- Asset.sol
| |-- Table.sol
||-- tool
|-- asset_run.sh // 项目运行脚本
  • 项目引入Web3SDK

在build.gradle文件中引入了fisco-bcos-java-sdk

1
2
3
4
dependencies {
compile ("org.fisco-bcos.java-sdk:fisco-bcos-java-sdk:2.8.0-SNAPSHOT")
...
}
  • 证书与配置文件

将区块链节点的sdk目录下的证书和配置文件拷贝到项目

1
cp ~/fisco/nodes/127.0.0.1/sdk/* asset-app/src/test/resources/

业务应用开发

  • 初始化

初始化主要是构造bcosSDK客户端以及设置客户端证书

1
2
3
4
5
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
bcosSDK = context.getBean(BcosSDK.class);
client = bcosSDK.getClient(1);
cryptoKeyPair = client.getCryptoSuite().createKeyPair();
client.getCryptoSuite().setCryptoKeyPair(cryptoKeyPair);
  • 部署合约
1
Asset asset = Asset.deploy(client, cryptoKeyPair);
  • 存储和加载合约地址
1
2
3
4
5
6
7
// 存储合约地址
Properties prop = new Properties();
prop.setProperty("address", address);
// 获取配置文件
final Resource contractResource = new ClassPathResource("contract.properties");
FileOutputStream fileOutputStream = new FileOutputStream(contractResource.getFile());
prop.store(fileOutputStream, "contract address");
1
2
3
4
5
6
7
8
9
10
// 加载合约地址
Properties prop = new Properties();
final Resource contractResource = new ClassPathResource("contract.properties");
prop.load(contractResource.getInputStream());

String contractAddress = prop.getProperty("address");
if (contractAddress == null || contractAddress.trim().equals("")) {
throw new Exception(" load Asset contract address failed, please deploy it first. ");
}
return contractAddress;
  • 调用合约
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 加载合约地址并创建合约对象
String contractAddress = loadAssetAddr();
Asset asset = Asset.load(contractAddress, client, cryptoKeyPair);

// 处理结果
Tuple2<BigInteger, BigInteger> result = asset.select(assetAccount);
if (result.getValue1().compareTo(new BigInteger("0")) == 0) {
System.out.printf(" asset account %s, value %s \n", assetAccount, result.getValue2());
} else {
System.out.printf(" %s asset account is not exist \n", assetAccount);
}

// 处理事件
List<Asset.RegisterEventEventResponse> response = asset.getRegisterEventEvents(receipt);
if (!response.isEmpty()) {
if (response.get(0).ret.compareTo(new BigInteger("0")) == 0) {
}
}

运行和测试

  • 编译
1
2
3
4
# 切换到项目目录
$ cd ~/asset-app
# 编译项目
$ ./gradlew build

注:
如果编译过程中出现“Could not find tools.jar”错误,需要配置JAVA_HOME
如:

1
export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home
  • 部署Asset.sol合约
1
2
3
# 进入dist目录
$ cd dist
$ bash asset_run.sh deploy
  • 注册资产
1
2
3
4
$ bash asset_run.sh register Alice 100000
Register account successfully => account: Alice, value: 100000
$ bash asset_run.sh register Bob 100000
Register account successfully => account: Bob, value: 100000
  • 查询资产
1
2
3
4
$ bash asset_run.sh query Alice
account Alice, value 100000
$ bash asset_run.sh query Bob
account Bob, value 100000
  • 转移资产
1
2
3
4
5
6
$ bash asset_run.sh transfer Alice Bob  50000
Transfer successfully => from_account: Alice, to_account: Bob, amount: 50000
$ bash asset_run.sh query Alice
account Alice, value 50000
$ bash asset_run.sh query Bob
account Bob, value 150000