FISCO BCOS 技术文档¶
【 该版本(2.0.0)的技术文档只适用FISCO-BCOS V2.0及以上版本,FISCO-BCOS V1.3.x版本的技术文档请查看: 技术文档 】
FISCO BCOS 是一个稳定、高效、安全的区块链底层平台,经过了外部多家机构、多个应用,长时间在生产环境运行的实际检验。
[ TODO: 插入一个介绍视频 ]
平台介绍¶
FISCO BCOS平台是金融区块链合作联盟(深圳)(以下简称:金链盟)开源工作组以金融业务实践为参考样本,在BCOS开源平台基础上进行模块升级与功能重塑,深度定制的安全可控、适用于金融行业且完全开源的区块链底层平台。金链盟开源工作组的首批成员包括以下单位:微众银行、深证通、腾讯、华为、神州数码、四方精创、博彦科技、越秀金科、亦笔科技等9家单位。
联盟链的升华:分布式商业与公众联盟链¶
商业,本身是一种竞争、自由的经济活动。而自由竞争的结果,天然就容易导致优胜劣汰、垄断集中、甚至寻租。尤其是2008年全球金融危机发生后,“大而不倒Too Big to Fail”的弊病显现,也因此引发了一系列的技术变革与商业变革,启动了一轮从“集中式”走向“分布式”的时代浪潮。
在这个背景下,区块链技术在2008年萌芽成型,并逐渐发展成熟。通过区块链技术解决方案中的共识机制、分布式账本、加密算法、智能合约、点对点通信、分布式计算架构、分布式存储、隐私保护算法、跨链协议等技术模块,可以让商业模式中的参与各方实现了地位对等和互信合作,从而推动了从“信息互联网”到“信任互联网”的时代进步,也令商业模式全面走向“分布式”成为可能。
新型的“分布式商业”模式,按微众银行整理给出的定义,是一种由多个具有对等地位的商业利益共同体所建立的新型生产关系,是通过预设的透明规则进行组织管理、职能分工、价值交换、共同提供商品与服务并分享收益的新型经济活动行为。在主要表现特征上,分布式商业显现出多方参与、共享资源、智能协同、价值整合、模式透明、跨越国界等特点。一个成熟的分布式商业场景具备生产资料由多方持有、产品和服务能力由多方共同构建、商业过程中的相互关系对等,产品和利益分配规则透明等要求。
分布式商业与此前流行的连锁加盟型商业模式及共享商业模式的最大不同之处在于,起到中间链接桥梁作用的不是人或产品、不是信息平台、而只有客观的技术本身。诚然,如果技术不开源,确实也可能演变成新的垄断。因此,发展分布式商业必须始终保持技术开源的态度,各个参与方通过开源社区进行分工合作,就将不再存在话语权集中和垄断的可能性,弱肉强食的“丛林法则”在此就不复存在。这有助于中小微企业真正成为商业价值链的主角,从而激发经济增长动力、广泛提升就业、鼓励创业和创新,实现“反垄断”的人类商业终极理想。
发展开源区块链技术的深远意义已不言而喻,但技术路线的选择也至关重要。虽然最原始的区块链技术起源于虚拟货币及公有链项目,但公有链的项目方往往以融资为目的,其用户则是以价格交易获利为目标,导致各方更多是关注币价的涨跌而非区块链的真正应用能力。由于公有链的代币实质上是“类货币”与“类证券”,已经被中国的监管部门严厉叫停。当潮水退去、大浪淘沙后,联盟链技术已肩负起推动区块链技术继续前行的重任。2018年,业界更是提出“公众联盟链”的发展路线,呼吁联盟链应该积极开放开源,从较为封闭的联盟内或公司内走向大众,让普罗大众真正感受到区块链带来的体验提升、效率提升、成本下降、信任增强、数据互换、责任追溯等好处,实现分布式商业的愿景。
新一代的公众联盟链,对于区块链底层技术提出了新的要求,除了标准的区块链特性之外,还有几个方面仍需要重点加强:首先,由于公众联盟链并不是单一链条,所以需要具备支持多链并行以及跨链通信的技术,同时需能够支撑来自互联网海量交易请求的能力。其次,需要具备快速、低成本地组建联盟和建链的能力,便于各个需求方能够高效建立联盟链网络,让企业间建链合作变得像现在建立“聊天群”一样高效便捷。最后,需要开源和开放,实现联盟成员之间的充分信任。公众联盟链不仅有利于降低企业快速试错的成本,有效提升商业上的容错性,也促进商业社会朝着可信化、透明化的方向深化发展,全面降低合作的操作风险、道德风险、信用风险、信息保护风险等。秉持以上的目标与愿景,我们正式发布了基于“公众联盟链”技术路线的FISCO BCOS 2.0版本。
FISCO BCOS 2.0¶
FISCO BCOS 2.0版本在原有基础上进行架构升级和优化,在可扩展性、性能、易用性等方面取得了重大突破,其中包括:
- 实现群组架构,在多个节点组成的一个全局网络中,可以存在多个节点子集组成的子网络,这些子网络维护一个独立的账本。这些账本之间的共识、存储都是相互独立的,具备良好的扩展性和安全性。在群组架构中,可以更好地实现平行扩展,满足金融级高频交易场景的需求。同时,群组架构可以快速支持组链需求,极大降低运维难度,真正能够实现企业间建链就像建“聊天群”一样简便。
- 支持分布式存储,使存储突破单机限制,支持横向扩展。计算和存储分离,提高了系统健壮性,即使节点执行服务器故障,数据也不会受影响。分布式存储定义了标准的数据访问CRUD接口,可以适配多种存储系统,同时支持SQL和NoSQL两种数据管理方式,可以更简便地支持多种业务场景。
- 实现预编译合约框架,突破EVM性能瓶颈。支持交易并发处理,大幅提升交易处理吞吐量。预编译合约采用C++实现,内置于底层系统中,区块链自动识别调用合约的交易互斥信息,构建DAG依赖,规划出一个高效的并行交易执行路径。最佳情况下,性能提升N倍(N=CPU核数)。
- 另外,FISCO BCOS 2.0版本持续在网络传输模型、计算存储流程等方面进行优化,对性能提升提供巨大帮助。在架构方面,在存储、网络、计算三个角度,围绕高可用性和高易用性进行持续升级。基于模块化、分层、可插拔等设计原则,持续对核心模块进行重塑升级,保证系统健壮性。
更多2.0版本的特性将在后续章节深入展开介绍,请看2.0新版介绍。
FISCO BCOS 1.0¶
回顾FISCO BCOS的演进历程,我们一直致力于达到性能、安全、可用性与合规的平衡。
- 在性能方面,FISCO BCOS 在整体架构和交易处理等方面都进行了大量的优化,包括采用了高效的共识算法,把能并行的计算并行化,减少重复计算,对关键计算单元进行升级等。更进一步地,其性能的核心突破点不仅仅在于单链,更在于基于单链性能优化架构设计,并实现灵活、高效、可靠、安全的并行计算和可平行扩展的能力。这帮助开发者能够灵活地根据自己业务场景的实际需要,通过简单增加机器,达到自己需要的性能。总体上,FISCO BCOS平台优化了网络通信模型,采用拜占庭容错共识机制,结合多链架构和跨链交互方案,可解决并发访问和热点帐户的性能痛点,从而满足金融级高频交易场景需求。
- 在安全性方面,FISCO BCOS 平台通过节点准入控制、可靠的密钥管理、灵活的权限控制,在应用、存储、网络、主机层实现全面的安全保障。在隐私保护的设计上,支持权限管理、物理隔离,支持国密算法(国家密码局认证的标准算法),同时也对外开源了包括同态加密、零知识证明、群签名、环签名等多种隐私保护算法的实现方案。
- 在可用性方面,FISCO BCOS设计为7×24小时运行,达到金融级高可用性。在监管支持方面,可支持监管和审计机构作为观察节点加入,获取实时数据进行监管审计。此外,还提供了各种开发接口,方便开发者编写和调用智能合约。
总结¶
实践之中出真知,FISCO BCOS经过了外部多家机构、多个应用,长时间在生产环境运行的实际检验,已经成长为一个稳定、高效、安全的区块链底层平台。
本文档后续内容将详细介绍FISCO BCOS 2.0版本的构建、安装、智能合约部署、调用等教程,以及深入介绍FISCO BCOS 2.0版本整体架构和各模块的设计方案。
2.0版本新特性¶
群组架构¶
2.0版本中新增了群组架构,用于克服系统吞吐能力的瓶颈。
传统的区块链平台,整个网络维护一个账本,所有节点参与到这个账本的共识和存储,系统的吞吐能力无法横向扩展。
群组架构允许网络中存在多个不同的账本,每个账本是一个独立的小组,节点可以选择加入某些小组,参与到该组账本的共识和存储,随着小组数量的增加,系统的吞吐能力能够横向扩展。
群组架构中,各群组独立执行共识流程,这样的设计设计考虑了性能、隐私性和扩展性。如所有群组采用同一个共识模块,一方面共识模块会成为性能的瓶颈,在参与节点多,系统吞吐量大的情况下,共识计算无法扩展。 另一方面,如果所有参与机构都加入共识,相当于在共识环节打破了群组边界,群组内的交易被其他机构知晓,如果只由部分“权威”机构参与共识,则存在中心化的风险。所以,按群组的粒度划分,由群组内参与者决定如何进行共识,一个群组内的共识不受其他群组影响,群组内维护自己的交易事务和数据,使得各群组之间解除耦合,独立运作,也便于进行横向扩展。
群组架构也有别与传统的多链架构,传统多链架构中,每条链在物理上和逻辑上都相互独立,一个节点只能参与其中一条链,在运维和管理上都导致成本成倍增加。 群组架构的设计,一个组相当于一条链,实现了传统多链的扩展目的,同时一个节点可以参与到多条链,能够极大地简化运维复杂度,降低管理成本。
另外,FISCO BCOS还将持续基于群组架构,实现动态管理和跨链服务,实现企业间建立联盟和组建链像建“聊天群”一样便利。
分布式存储¶
2.0版本中新增了对分布式数据存储的支持,克服了本地化数据存储的诸多限制。
1.0版本中,节点采用LevelDB将数据存储于本地,这种模式受限于本地磁盘大小,当业务量增大时数据膨胀,数据迁移也是一个非常复杂的问题,业界大部分的区块链平台也面临类似问题。
通过支持节点将数据存储在远端分布式系统中,分布式存储方案有以下优点:
- 支持多种存储引擎,选用高可用的分布式存储系统,可以支持数据简便快速地扩容;
- 将计算和数据隔离,节点故障不会导致数据异常;
- 数据在远端存储,数据可以在更安全的隔离区存储,这在很多场景中非常有意义;
- 分布式存储不仅支持Key-Value形式,还支持SQL方式,使得业务开发更为简便;
- 世界状态的存储也从原来的MPT存储结构转为分布式存储,避免了世界状态急剧膨胀导致性能下降的问题。
- 优化了数据存储的结构,更节约存储空间,存取效率更高。
同时,2.0版本仍然兼容1.0版本的本地存储模式。更多关于存储介绍,请参考分布式存储设计文档
预编译合约¶
2.0版本中新增了对预编译合约的支持,大幅提升了智能合约的执行效率。
FISCO BCOS 1.0从以太坊技术体系继承而来,合约采用Solidity编写。 Solidity合约需要通过特殊的编译器(Solc)编译成二进制字节码以及ABI(接口描述文件),二进制字节码部署在链上,由EVM(以太坊虚拟机)解释执行。 Solidity具有很多优良的特性,比如图灵完备、可扩展性强等等。但是Solidity合约也有一些缺陷,包括执行效率较低,部署流程复杂等。
2.0实现了一套在底层通过C++实现的预编译合约框架,可以方便扩展用户所需的合约。预编译合约执行效率远远高于Solidity合约,突破了EVM的执行性能限制。 同时,预编译合约兼容Solidity的调用方式,使用方式保持一致。
FISCO BCOS 1.0的所有系统合约已经采用预编译合约方式实现,天然集成在底层平台,无需用户手动部署。 另外,还有类似CRUD操作等也由预编译合约实现,更多预编译合约的介绍,请参考预编译设计文档和预编译合约开发文档
CRUD合约¶
2.0版本中新增了符合CRUD接口的合约接口规范,简化了将主流的面向SQL设计的商业应用迁移到区块链上的成本。
1.0版本的合约,数据存储在合约的成员变量,例如采用Map/List等形式,合约的逻辑直接面向这些存储变量。 2.0版本基于预编译合约,实现了一套CRUD基本数据访问接口规范合约,基于CRUD接口编写业务合约,实现传统面向SQL方式的业务开发流程,这种方式有下面几个好处:
- 与传统业务开发模式类似,简化了合约开发学习成本;
- 合约只需关系核心逻辑,存储与计算分离,方便合约升级;
- CRUD底层逻辑采用预编译合约实现,采用分布式存储,效率更高;
同时,2.0版本仍然兼容1.0版本的合约,更多关于CRUD合约的介绍,请参考CRUD合约开发
并行交易处理¶
2.0版本中新增了合约交易的并行处理机制,进一步提升了合约的并发吞吐量。
1.0版本以及大部分业界传统区块链平台,交易是被打包成一个区块,在一个区块中交易顺序串行执行的。 2.0版本基于预编译合约,实现一套并行交易处理模型,基于这个模型可以自定义交易互斥变量。 在区块执行过程中,系统将会根据交易互斥变量自动构建交易依赖关系图——DAG,基于DAG并行执行交易,最好情况下性能可提升数倍(取决于CPU核数)。
更多关于并行交易处理的介绍,请参考并行交易处理模型
虚拟机¶
2.0版本引入了最新的以太坊虚拟机版本,支持Solidity 0.5版本。同时,引入了evmc扩展框架,支持扩展不同虚拟机引擎。 底层内部集成支持interpreter虚拟机,未来可扩展支持WASM/JIT等虚拟机。
更多关于虚拟机的介绍,请参考虚拟机设计文档
控制台¶
2.0版本新增控制台,便于用户快速接入使用区块链功能。
控制台是2.0重要的交互式客户端工具,它拥有丰富的命令,可以查询区块链状态,管理区块链节点,部署并调用合约等功能。 相比于传统的nodejs等脚本工具,控制台安装简单、使用体验更好。详细请查看控制台使用手册
密钥管理服务¶
2.0版本对落盘加密进行了重塑升级,开启落盘加密功能时,依赖KeyManager服务进行密钥管理,安全性更强。
KeyManager在Github开源发布,节点与KeyManager的交互协议是开放的,支持机构设计实现符合自身密钥管理规范的KeyManager服务,比如采用硬件加密机技术。 该部分更详细的文档请参考使用文档和设计文档
准入控制¶
2.0版本对准入机制进行了重塑升级,包括网络准入机制和群组准入机制,在不同维度对链和数据访问进行安全控制。
采用新的权限控制体系,基于表进行访问权限的设计,另外还支持CA黑名单机制,可以实现对作恶/故障节点的屏蔽。 详情请查看准入机制设计文档
异步事件¶
2.0版本同时支持交易上链异步通知、区块上链异步通知以及自定义的AMOP消息通知等机制。
模块重塑¶
2.0版本对核心模块进行升级重塑,进行模块化的单元测试和端对端集成测试,支持自动化持续集成和持续部署。
关键概念¶
区块链是由多个学科交叉组合形成的一门技术,本章将阐述区块链相关的基本概念,对涉及的基本理论进行科普介绍。如果您已经对这些基本技术很熟悉,可以跳过本章直接进入快速入门。
区块链是什么¶
区块链(blockchain)是在比特币之后提出的一个概念,在中本聪关于比特币的论文(链接)中没有直接引入blockchain的概念,而是以chain of block来描述一种数据结构。
chain of block是指由多个区块通过哈希(hash)串联成一条链式结构的数据组织方式。区块链则是采用多项技术交叉组合,维护管理这个chain of block数据结构,形成一个不可篡改的分布式账本的综合技术领域。
快速入门¶
FISCO BCOS二进制提供以下获取方式,用户可以自由选择安装方式。
为了简化安装与配置,FISCO BCOS提供了build_chain脚本来帮助用户快读搭建FISCO BCOS联盟链,该脚本默认从GitHub下载master
分支最新版本二进制搭建。
初次接触FISCO BCOS的用户建议从Hello World教程开始,通过在本机搭建FISCO BCOS以及部署和调用Hello World合约快速入门。
Java SDK¶
【 该版本(2.0)的技术文档只适用Java SDK V2.0及以上版本(与FISCO-BCOS V2.0及以上版本适配),Java SDK V1.2.x版本的技术文档请查看 Java SDK V1.2.x版本技术文档 】
Java SDK提供访问FISCO BCOS节点的Java API。
Welcome to Java SDK
环境要求¶
Important
- java版本
要求 jdk1.8+,推荐使用jdk8u181
- FISCO BCOS区块链环境搭建
- 网络连通性
检查Java SDK连接的FISCO BCOS节点channel_listen_port是否能telnet通,若telnet不通,需要检查网络连通性和安全策略。
Java SDK配置与使用¶
Java应用引入SDK¶
通过gradle或maven引入SDK到java应用
gradle:
compile ('org.fisco-bcos:web3sdk:2.0.2')
maven:
<dependency>
<groupId>org.fisco-bcos</groupId>
<artifactId>web3sdk</artifactId>
<version>2.0.2</version>
</dependency>
由于引入了以太坊的solidity编译器相关jar包,需要在Java应用的gradle配置文件build.gradle中添加以太坊的远程仓库
repositories {
mavenCentral()
maven { url "https://dl.bintray.com/ethereum/maven/" }
}
配置SDK¶
FISCO BCOS节点证书配置¶
FISCO-BCOS作为联盟链,其SDK连接区块链节点需要通过证书(ca.crt、node.crt)和私钥(node.key)进行双向认证。
配置文件设置¶
Java应用的配置文件需要做相关配置。值得关注的是,FISCO-BCOS2.0支持多群组功能,SDK需要配置群组的节点信息。将以Spring项目和Spring Boot项目为例,提供配置指引。
Spring项目配置¶
提供Spring项目中关于applicationContext.xml的配置如下图所示,其中红框标记的内容根据区块链节点配置做相应修改。
applicationContext.xml配置项详细说明:
- encryptType: 国密算法开关(默认为0)
- 0: 不使用国密算法发交易
- 1: 使用国密算法发交易(开启国密功能,需要连接的区块链节点是国密节点,搭建国密版FISCO BCOS区块链参考这里)
- groupChannelConnectionsConfig:
- 配置待连接的群组,可以配置一个或多个群组,每个群组需要配置群组ID
- 每个群组可以配置一个或多个节点,设置群组节点的配置文件config.ini中
[rpc]
部分的listen_ip
和channel_listen_port
。
- channelService: 通过指定群组ID配置SDK实际连接的群组,指定的群组ID是groupChannelConnectionsConfig配置中的群组ID。SDK会与群组中配置的节点均建立连接,然后随机选择一个节点发送请求。
Spring Boot项目配置¶
提供Spring Boot项目中关于application.yml的配置如下图所示,其中红框标记的内容根据区块链节点配置做相应修改。
application.yml配置项与applicationContext.xml配置项相对应,详细介绍参考applicationContext.xml配置说明。
使用SDK¶
Spring项目开发指引¶
调用SDK的API(参考SDK API列表)设置或查询相关的区块链数据。¶
- 调用SDK Web3j的API:需要加载配置文件,SDK与区块链节点建立连接。获取web3j对象,根据Web3j对象调用相关API。示例代码如下:
//读取配置文件,SDK与区块链节点建立连接
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
Service service = context.getBean(Service.class);
service.run();
ChannelEthereumService channelEthereumService = new ChannelEthereumService();
channelEthereumService.setChannelService(service);
//获取Web3j对象
Web3j web3j = Web3j.build(channelEthereumService, service.getGroupId());
//通过Web3j对象调用API接口getBlockNumber
BigInteger blockNumber = web3j.getBlockNumber().send().getBlockNumber();
System.out.println(blockNumber);
- 调用SDK Precompiled的API:需要加载配置文件,SDK与区块链节点建立连接。获取SDK Precompiled Service对象,调用相关的API。示例代码如下:
//读取配置文件,SDK与区块链节点建立连接,获取Web3j对象
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
Service service = context.getBean(Service.class);
service.run();
ChannelEthereumService channelEthereumService = new ChannelEthereumService();
channelEthereumService.setChannelService(service);
Web3j web3j = Web3j.build(channelEthereumService, service.getGroupId());
//填入用户私钥,用于交易签名
Credentials credentials = Credentials.create("b83261efa42895c38c6c2364ca878f43e77f3cddbc922bf57d0d48070f79feb6");
//获取SystemConfigService对象
SystemConfigSerivce systemConfigSerivce = new SystemConfigSerivce(web3j, credentials);
//通过SystemConfigService对象调用API接口setValueByKey
String result = systemConfigSerivce.setValueByKey("tx_count_limit", "2000");
//通过Web3j对象调用API接口getSystemConfigByKey
String value = web3j.getSystemConfigByKey("tx_count_limit").send().getSystemConfigByKey();
System.out.println(value);
通过SDK部署并调用合约¶
使用SDK把Solidity合约文件转成相应Java合约文件。合约转换工作在SDK源码目录下进行,因此需要获取SDK源码,然后进行合约转换。
# 获取sdk源码
git clone https://github.com/FISCO-BCOS/web3sdk
cd web3sdk
git checkout release-2.0.1
合约转换步骤:
把编写的Solidity合约文件拷贝到src/test/resources/contract下,确保合约名和文件名保持一致。
在SDK根目录下执行转换合约命令:
bash sol2java
生成的Java合约文件在src/test/java/org/fisco/bcos/temp目录,生成的abi和bin文件在src/test/resources/solidity目录。
温馨提示:SDK默认支持的Solidity版本是0.4.25,如果编译0.5以上版本合约,需要做如下配置
修改build.gradle配置文件,注释0.4.25版编译器jar包,使用0.5.2版编译器jar包,修改后的配置如下:
// compile files('lib/solcJ-all-0.4.25-gm.jar') compile 'org.ethereum:solcJ-all:0.5.2' // compile 'org.ethereum:solcJ-all:0.4.25'
删除src/test/resources/contract目录下版本为0.4.x的Solidity合约文件,将版本为0.5.x的Solidity合约文件拷贝到src/test/resources/contract目录下。
在SDK根目录下执行转换合约命令:
bash sol2java
生成的Java合约文件在src/test/java/org/fisco/bcos/temp目录,生成的abi和bin文件在src/test/resources/solidity目录。
SDK的核心功能是部署/加载合约,然后调用合约相关接口,实现相关业务功能。部署合约调用Java合约类的deploy方法,获取合约对象。通过合约对象可以调用getContractAddress方法获取部署合约的地址以及调用该合约的其他方法实现业务功能。如果合约已部署,则通过部署的合约地址可以调用load方法加载合约对象,然后调用该合约的相关方法。
//读取配置文件,sdk与区块链节点建立连接,获取web3j对象
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
Service service = context.getBean(Service.class);
service.run();
ChannelEthereumService channelEthereumService = new ChannelEthereumService();
channelEthereumService.setChannelService(service);
channelEthereumService.setTimeout(10000);
Web3j web3j = Web3j.build(channelEthereumService, service.getGroupId());
//准备部署和调用合约的参数
BigInteger gasPrice = new BigInteger("300000000");
BigInteger gasLimit = new BigInteger("300000000");
Credentials credentials = Credentials.create("b83261efa42895c38c6c2364ca878f43e77f3cddbc922bf57d0d48070f79feb6");
//部署合约
YourSmartContract contract = YourSmartContract.deploy(web3j, credentials, new StaticGasProvider(gasPrice, gasLimit)).send();
//根据合约地址加载合约
//YourSmartContract contract = YourSmartContract.load(address, web3j, credentials, new StaticGasProvider(gasPrice, gasLimit));
//调用合约方法发送交易
TransactionReceipt transactionReceipt = contract.someMethod(<param1>, ...).send();
//查询合约方法查询该合约的数据状态
Type result = contract.someMethod(<param1>, ...).send();
Spring Boot项目开发指引¶
提供spring-boot-starter示例项目供参考。Spring Boot项目开发与Spring项目开发类似,其主要区别在于配置文件方式的差异。该示例项目提供丰富的测试案例,具体描述参考示例项目的README文档。
SDK国密功能使用¶
- 前置条件:FISCO BCOS区块链采用国密算法,搭建国密版的FISCO BCOS区块链请参考国密使用手册。
- 启用国密功能:application.xml/application.yml配置文件中将encryptType属性设置为1。
国密版SDK调用API的方式与普通版SDK调用API的方式相同,其差异在于国密版SDK需要生成国密版的Java合约文件。SDK源码的lib目录下已有国密版的编译器jar包,用于将Solidity合约文件转为国密版的Java合约文件。因此,使用国密版SDK只需要修改SDK源码根目录下的build.gradle文件,注释掉普通版编译器jar包,引入国密编译器jar包。
compile files('lib/solcJ-all-0.4.25-gm.jar')
// compile 'org.ethereum:solcJ-all:0.4.25'
// compile 'org.ethereum:solcJ-all:0.5.2'
Solidity合约文件转换为国密版Java合约文件的步骤、部署和调用国密版合约的方法均与普通版SDK相同。
Java SDK API¶
Java SDK API主要分为Web3j API和Precompiled Service API。其中Web3j API可以查询区块链相关的状态,发送和查询交易信息;Precompiled Service API可以管理区块链相关配置以及实现特定功能。
Web3j API¶
Web3j API是由web3j对象调用的FISCO BCOS的RPC API,其API名称与RPC API相同,参考RPC API文档。调用Web3j API示例参考SDK配置与使用。
Precompiled Service API¶
Precompiled合约是FISCO BCOS底层通过C++实现的一种高效智能合约。SDK已提供Precompiled合约对应的Java接口,控制台通过调用这些Java接口实现了相关的操作命令,体验控制台,参考控制台手册。SDK提供Precompiled对应的Service类,分别是分布式控制权限相关的AuthorityService,CNS相关的CnsService,系统属性配置相关的SystemConfigService和节点类型配置相关ConsensusService。
设置和移除接口返回json字符串,包含code和msg字段,当无权限操作时,其code定义-1,msg定义为“non-authorized”。当成功操作时,其code为1(影响1条记录),msg为“success”。其他返回码如下:
code | msg |
---|---|
-30 | table name and address exist |
-31 | table name and address does not exist |
-40 | invalid nodeID |
-41 | last sealer cannot be removed |
-42 | nodeID is not in network |
-43 | nodeID is not in group peers |
-44 | nodeID is already in sealer list |
-45 | nodeID is already in observer list |
-50 | address and version exist |
-51 | version exceeds maximum(40) length |
-60 | set invalid configuration values |
调用Precompiled Service API示例参考Java SDK配置与使用。
AuthorityService¶
SDK提供对分布式控制权限的支持,AuthorityService可以配置权限信息,其API如下:
- public String addUserTableManager(String tableName, String address): 根据用户表名和外部账号地址设置权限信息。
- public String removeUserTableManager(String tableName, String address): 根据用户表名和外部账号地址去除权限信息。
- public List<AuthorityInfo> queryUserTableManager(String tableName): 根据用户表名查询设置的权限记录列表(每条记录包含外部账号地址和生效块高)。
- public String addDeployAndCreateManager(String address): 增加外部账号地址的部署合约和创建用户表权限。
- public String removeDeployAndCreateManager(String address): 移除外部账号地址的部署合约和创建用户表权限。
- public List<AuthorityInfo> queryDeployAndCreateManager(): 查询拥有部署合约和创建用户表权限的权限记录列表。
- public String addAuthorityManager(String address): 增加外部账号地址的管理权限的权限。
- public String removeAuthorityManager(String address): 移除外部账号地址的管理权限的权限。
- public List<AuthorityInfo> queryAuthorityManager(): 查询拥有管理权限的权限记录列表。
- public String addNodeManager(String address): 增加外部账号地址的节点管理权限。
- public String removeNodeManager(String address): 移除外部账号地址的节点管理权限。
- public List<AuthorityInfo> queryNodeManager(): 查询拥有节点管理的权限记录列表。
- public String addCNSManager(String address): 增加外部账号地址的使用CNS权限。
- public String removeCNSManager(String address): 移除外部账号地址的使用CNS权限。
- public List<AuthorityInfo> queryCNSManager(): 查询拥有使用CNS的权限记录列表。
- public String addSysConfig(String address): 增加外部账号地址的系统参数管理权限。
- public String removeSysConfig(String address): 移除外部账号地址的系统参数管理权限。
- public List<AuthorityInfo> querySysConfig(): 查询拥有系统参数管理的权限记录列表。
CnsService¶
SDK提供对CNS的支持。CnsService可以配置CNS信息,其API如下:
- String registerCns(String name, String version, String address, String abi): 根据合约名、合约版本号、合约地址和合约abi注册CNS信息。
- String getAddressByContractNameAndVersion(String contractNameAndVersion): 根据合约名和合约版本号(合约名和合约版本号用英文冒号连接)查询合约地址。
- List<CnsInfo> queryCnsByName(String name): 根据合约名查询CNS信息。
- List<CnsInfo> queryCnsByNameAndVersion(String name, String version): 根据合约名和合约版本号查询CNS信息。
SystemConfigSerivce¶
SDK提供对系统配置的支持。SystemConfigSerivce可以配置系统属性值(目前支持tx_count_limit和tx_gas_limit属性的设置),其API如下:
- String setValueByKey(String key, String value): 根据键设置对应的值(查询键对应的值,参考Web3j API中的getSystemConfigByKey接口)。
用户手册¶
用户手册提供FISCO BCOS所有功能的操作介绍以及各种特性的配置说明,用户根据需求查看即可。
Hello World¶
本教程面向初次接触FISCO-BCOS的用户,通过在本机部署FISCO-BCOS以及部署和调用Hello World合约,帮助初学者快速入门FISCO BCOS。
首次部署FISCO-BCOS联盟链¶
使用build_chain
脚本¶
本节使用build_chain
脚本在本地搭建一条4节点的FISCO-BCOS链,以Ubuntu 16.04
系统为例操作。本节使用预编译的静态fisco-bcos
二进制,在CentOS 7和Ubuntu 16.04上经过测试。(理论上静态二进制可在任意Linux操作系统上运行)。
- 准备环境
build_chain
脚本依赖于openssl
,推荐根据自己操作系统安装openssl 1.0.2
以上版本。
# Ubuntu16安装依赖
$ sudo apt install -y openssl curl
# 准备环境
$ cd ~ && mkdir fisco && cd fisco
# 下载build_chain.sh脚本
$ curl -LO https://raw.githubusercontent.com/FISCO-BCOS/FISCO-BCOS/release-2.0.1/tools/build_chain.sh && chmod u+x build_chain.sh
# 准备fisco-bcos二进制
$ bash <(curl -s https://raw.githubusercontent.com/FISCO-BCOS/FISCO-BCOS/release-2.0.1/tools/ci/download_bin.sh) -b release-2.0.1
# 检查二进制是否可执行 执行下述命令,看是否输出版本信息
$ ./bin/fisco-bcos -v
执行完上述步骤后,fisco目录下结构如下
fisco
├── bin
│ └── fisco-bcos
└── build_chain.sh
- 搭建4节点FISCO-BCOS链
# 生成一条4节点的FISCO链 4个节点都属于group1 下面指令在fisco目录下执行
# -e 指定fisco-bcos路径 -p指定起始端口,分别是p2p_port,channel_port,jsonrpc_port
# 根据下面的指令,需要保证机器的30300-30303 20200-20203 8545-8548端口没有被占用
$ ./build_chain.sh -e bin/fisco-bcos -l "127.0.0.1:4" -p 30300,20200,8545
关于build-chain
脚本选项,请参考这里。命令正常执行会输出All completed
。(如果没有输出,则参考nodes/build.log
检查)。
Generating CA key...
==============================================================
Generating keys ...
Processing IP:127.0.0.1 Total:4 Agency:agency Groups:1
==============================================================
Generating configurations...
Processing IP:127.0.0.1 Total:4 Agency:agency Groups:1
==============================================================
[INFO] FISCO-BCOS Path : bin/fisco-bcos
[INFO] Start Port : 30300 20200 8545
[INFO] Server IP : 127.0.0.1:4
[INFO] State Type : storage
[INFO] RPC listen IP : 127.0.0.1
[INFO] Output Dir : /mnt/d/fisco/nodes
[INFO] CA Key Path : /mnt/d/fisco/nodes/cert/ca.key
==============================================================
[INFO] All completed. Files in /mnt/d/fisco/nodes
启动并检查状态¶
- 启动FISCO-BCOS链
# 进入节点目录 当前目录fisco
$ cd nodes/127.0.0.1
# 启动所有节点
$ ./start_all.sh
- 检查进程及端口监听
# 检查进程是否启动 如果进程数不为4,那么进程没启动的原因一般是端口被占用
$ ps -ef | grep -v grep | grep fisco
fisco 5453 1 1 17:11 pts/0 00:00:02 /home/fisco/fisco/nodes/127.0.0.1/node0/../fisco-bcos -c config.ini
fisco 5459 1 1 17:11 pts/0 00:00:02 /home/fisco/fisco/nodes/127.0.0.1/node1/../fisco-bcos -c config.ini
fisco 5464 1 1 17:11 pts/0 00:00:02 /home/fisco/fisco/nodes/127.0.0.1/node2/../fisco-bcos -c config.ini
fisco 5476 1 1 17:11 pts/0 00:00:02 /home/fisco/fisco/nodes/127.0.0.1/node3/../fisco-bcos -c config.ini
# 检查监听的端口,当前版本每个节点监听3个端口,分别用于p2p、jsonrpc、channel
$ netstat -ntlp | grep fisco
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
tcp 0 0 0.0.0.0:30300 0.0.0.0:* LISTEN 5453/fisco-bcos
tcp 0 0 127.0.0.1:20200 0.0.0.0:* LISTEN 5453/fisco-bcos
tcp 0 0 127.0.0.1:8545 0.0.0.0:* LISTEN 5453/fisco-bcos
tcp 0 0 0.0.0.0:30301 0.0.0.0:* LISTEN 5459/fisco-bcos
tcp 0 0 127.0.0.1:20201 0.0.0.0:* LISTEN 5459/fisco-bcos
tcp 0 0 127.0.0.1:8546 0.0.0.0:* LISTEN 5459/fisco-bcos
tcp 0 0 0.0.0.0:30302 0.0.0.0:* LISTEN 5464/fisco-bcos
tcp 0 0 127.0.0.1:20202 0.0.0.0:* LISTEN 5464/fisco-bcos
tcp 0 0 127.0.0.1:8547 0.0.0.0:* LISTEN 5464/fisco-bcos
tcp 0 0 0.0.0.0:30303 0.0.0.0:* LISTEN 5476/fisco-bcos
tcp 0 0 127.0.0.1:20203 0.0.0.0:* LISTEN 5476/fisco-bcos
tcp 0 0 127.0.0.1:8548 0.0.0.0:* LISTEN 5476/fisco-bcos
- 检查日志输出
# 查看节点node0链接的节点数,从下面的输出可以看出node0与另外3个节点有链接
$ tail -f node0/log/log* | grep connected
info|2019-01-21 17:30:58.316769| [P2P][Service] heartBeat connected count,size=3
info|2019-01-21 17:31:08.316922| [P2P][Service] heartBeat connected count,size=3
info|2019-01-21 17:31:18.317105| [P2P][Service] heartBeat connected count,size=3
# 检查是否在共识,如果不停输出++++Generating seal 表示正常输出
$ tail -f node0/log/log* | grep +++
info|2019-01-21 17:23:32.576197| [g:1][p:264][CONSENSUS][SEALER]++++++++++++++++Generating seal on,blkNum=1,tx=0,myIdx=2,hash=13dcd2da...
info|2019-01-21 17:23:36.592280| [g:1][p:264][CONSENSUS][SEALER]++++++++++++++++Generating seal on,blkNum=1,tx=0,myIdx=2,hash=31d21ab7...
info|2019-01-21 17:23:40.612241| [g:1][p:264][CONSENSUS][SEALER]++++++++++++++++Generating seal on,blkNum=1,tx=0,myIdx=2,hash=49d0e830...
使用控制台¶
控制台通过Java SDK链接FISCO BCOS节点,实现查询区块链状态、部署调用合约等功能,能够快速获取到所需要的信息。控制台依赖于Java8以上版本(CentOS请安装Oracle Java 8以上版本),对于Ubuntu 16.04系统安装openjdk 8即可。控制台详细文档参考这里。
- 准备依赖
# 回到fisco目录
$ cd ~/fisco
# 安装openjdk
$ sudo apt install -y default-jdk
$ curl -LO https://github.com/FISCO-BCOS/LargeFiles/raw/master/tools/console.tar.gz
$ tar -zxf console.tar.gz && chmod u+x console/start
# 配置控制台证书
$ cp nodes/127.0.0.1/sdk/* console/conf/
- 启动控制台
# # 回到fisco目录
$ cd ~/fisco/console
$ bash ./start
# 输出下述信息表明启动成功
=============================================================================================
Welcome to FISCO BCOS console!
Type 'help' or 'h' for help. Type 'quit' or 'q' to quit console.
________ ______ ______ ______ ______ _______ ______ ______ ______
| \| \ / \ / \ / \ | \ / \ / \ / \
| $$$$$$$$ \$$$$$$| $$$$$$\| $$$$$$\| $$$$$$\ | $$$$$$$\| $$$$$$\| $$$$$$\| $$$$$$\
| $$__ | $$ | $$___\$$| $$ \$$| $$ | $$ | $$__/ $$| $$ \$$| $$ | $$| $$___\$$
| $$ \ | $$ \$$ \ | $$ | $$ | $$ | $$ $$| $$ | $$ | $$ \$$ \
| $$$$$ | $$ _\$$$$$$\| $$ __ | $$ | $$ | $$$$$$$\| $$ __ | $$ | $$ _\$$$$$$\
| $$ _| $$_ | \__| $$| $$__/ \| $$__/ $$ | $$__/ $$| $$__/ \| $$__/ $$| \__| $$
| $$ | $$ \ \$$ $$ \$$ $$ \$$ $$ | $$ $$ \$$ $$ \$$ $$ \$$ $$
\$$ \$$$$$$ \$$$$$$ \$$$$$$ \$$$$$$ \$$$$$$$ \$$$$$$ \$$$$$$ \$$$$$$
=============================================================================================
- 使用控制台获取信息
# 获取客户端版本
> getClientVersion
{
"Build Time":"20190121 06:21:05",
"Build Type":"Linux/clang/Debug",
"FISCO-BCOS Version":"2.0.0",
"Git Branch":"release-2.0.0",
"Git Commit Hash":"a322f0bff5cb395157fb5734219fcb2f2686ef08"
}
# 获取节点链接信息
> getPeers
[
{
"IPAndPort":"127.0.0.1:49948",
"NodeID":"b5872eff0569903d71330ab7bc85c5a8be03e80b70746ec33cafe27cc4f6f8a71f8c84fd8af9d7912cb5ba068901fe4131ef69b74cc773cdfb318ab11968e41f",
"Topic":[]
},
{
"IPAndPort":"127.0.0.1:49940",
"NodeID":"912126291183b673c537153cf19bf5512d5355d8edea7864496c257630d01103d89ae26d17740daebdd20cbc645c9a96d23c9fd4c31d16bccf1037313f74bb1d",
"Topic":[]
},
{
"IPAndPort":"127.0.0.1:49932",
"NodeID":"db75ab16ed7afa966447c403ca2587853237b0d9f942ba6fa551dc67ed6822d88da01a1e4da9b51aedafb8c64e9d208d9d3e271f8421f4813dcbc96a07d6a603",
"Topic":[]
}
]
部署调用Hello World合约¶
HelloWorld合约¶
HelloWorld合约提供两个接口,分别是get()
和set()
,用于获取/设置合约变量name
。合约内容如下
pragma solidity ^0.4.2;
contract HelloWorld{
string name;
function HelloWorld(){
name = "Hello, World!";
}
function get()constant returns(string){
return name;
}
function set(string n){
name = n;
}
}
使用控制台部署HelloWorld合约¶
为了方便用户快速体验,HelloWorld合约已经通过Solidity编译并转为Java接口内置于控制台中,所以接下来参考下面命令部署即可。关于Solidity合约转Java接口,参考这里。
# 在控制台输入以下指令 部署成功则返回合约地址
> deploy HelloWorld
0xb3c223fc0bf6646959f254ac4e4a7e355b50a344
使用控制台调用HelloWorld合约¶
# 查看当前块高
> getBlockNumber
1
# 调用get接口获取name变量
> call HelloWorld 0xb3c223fc0bf6646959f254ac4e4a7e355b50a344 get
Hello, World!
# 调用set设置name
> call HelloWorld 0xb3c223fc0bf6646959f254ac4e4a7e355b50a344 set "Hello,FISCO-BCOS"
0x21dca087cb3e44f44f9b882071ec6ecfcb500361cad36a52d39900ea359d0895
# 调用get接口获取name变量,检查设置是否生效
> call HelloWorld 0xb3c223fc0bf6646959f254ac4e4a7e355b50a344 get
Hello,FISCO-BCOS
# 查看当前块高
> getBlockNumber
2
建链脚本¶
脚本功能简介¶
build_chain
脚本用于快速生成一条链中节点的配置文件,脚本依赖于openssl
请根据自己的操作系统安装openssl 1.0.2
以上版本。- 快速体验可以使用
-l
选项指定节点IP和数目。-f
选项通过使用一个指定格式的配置文件,提供了创建更加复杂的链的功能。-l
和-f
选项必须指定一个且不可共存。 - 建议测试时使用
-T
和-i
选项选项,-T
开启log级别到DEBUG,-i
设置RPC和channel监听0.0.0.0
。p2p模块默认监听0.0.0.0
。
帮助¶
Usage:
-l <IP list> [Required] "ip1:nodeNum1,ip2:nodeNum2" e.g:"192.168.0.1:2,192.168.0.2:3"
-f <IP list file> [Optional] split by line, every line should be "ip:nodeNum agencyName groupList". eg "127.0.0.1:4 agency1 1,2"
-e <FISCO-BCOS binary path> Default download from GitHub
-o <Output Dir> Default ./nodes/
-p <Start Port> Default 30300,20200,8545 means p2p_port start from 30300, channel_port from 20200, jsonrpc_port from 8545
-i <Host ip> Default 127.0.0.1. If set -i, listen 0.0.0.0
-c <Consensus Algorithm> Default PBFT. If set -c, use Raft
-s <State type> Default storage. if set -s, use mpt
-g <Generate guomi nodes> Default no
-z <Generate tar packet> Default no
-t <Cert config file> Default auto generate
-T <Enable debug log> Default off. If set -T, enable debug log
-h Help
e.g
../tools/build_chain.sh -l "127.0.0.1:4"
Important
build_chain目标是让用户最快使用FISCO BCOS,对于企业级应用请参考 企业工具 。
选项介绍¶
l
选项: 用于指定要生成的链的IP列表以及每个IP下的节点数,以逗号分隔。脚本根据输入的参数生成对应的节点配置文件,其中每个节点的端口号默认从30300开始递增,所有节点属于同一个机构和Group。f
选项- 用于根据配置文件生成节点,相比于
l
选项支持更多的定制。 - 按行分割,每一行表示一个服务器,格式为
IP:NUM AgencyName GroupList
,每行内的项使用空格分割,不可有空行。 IP:NUM
表示机器的IP地址以及该机器上的节点数。AgencyName
表示机构名,用于指定使用的机构证书。GroupList
表示该行生成的节点所属的组,以,
分割。例如192.168.0.1:2 agency1 1,2
表示ip
为192.168.0.1
的机器上有两个节点,这两个节点属于机构agency1
,属于group1和group2。
- 用于根据配置文件生成节点,相比于
下面是一个配置文件的例子,每个配置项以空格分隔,其中GroupList
表示该服务器所属的组。
192.168.0.1:2 agency1 1,2
192.168.0.1:2 agency1 1,3
192.168.0.2:3 agency2 1
192.168.0.3:5 agency3 2,3
192.168.0.4:2 agency2 3
假设上述文件名为ipconf
,则使用下列命令建链
bash build_chain.sh -f ipconf -T -i
e
选项[Optional] 用于指定fisco-bcos
二进制所在的路径,脚本会将fisco-bcos
拷贝以IP为名的目录下。不指定时,默认从GitHub下载master
分支最新的二进制程序。o
选项[Optional] 指定生成的配置所在的目录。p
选项[Optional] 指定节点的起始端口,每个节点占用三个端口,分别是p2p,channel,jsonrpc使用,
分割端口,必须指定三个端口。同一个IP下的不同节点所使用端口从起始端口递增。 例如build_chain -l 127.0.0.1:2 -p 30300,20200,8545
,那么两个节点分别占用30300,20200,8545
和30301,20201,8546
。i
选项[Optional] 无参数选项,设置该选项时,设置节点的RPC和channel监听0.0.0.0
c
选项[Optional] 无参数选项,设置该选项时,设置节点的共识算法为Raft,默认设置为PBFT。s
选项[Optional] 无参数选项,设置该选项时,节点使用mptstate存储合约局部变量,默认使用storagestate存储合约局部变量。g
选项[Optional] 无参数选项,设置该选项时,编译国密版本。使用g
选项时要求二进制fisoc-bcos为国密版本。z
选项[Optional] 无参数选项,设置该选项时,生成节点的tar包。t
选项[Optional] 该选项用于指定生成证书时的证书配置文件。T
选项[Optional] 无参数选项,设置该选项时,设置节点的log级别为DEBUG。log详细内容参考这里
节点组织结构¶
- cert文件夹下存放链的根证书和机构证书。
- 以IP命名的文件夹下存储该服务器所有节点相关配置、
fisco-bcos
可执行文件、sdk所需的证书文件。 - 每个IP文件夹下的
node*
文件夹下存储节点所需的配置文件。其中config.ini
为节点的主配置,conf
目录下存储证书文件和群组相关配置。配置文件详情,请参考这里。每个节点中还提供两个脚本,用于启动和停止节点。 - 每个IP文件夹下的提供
start_all.sh
和stop_all.sh
两个脚本用于启动和停止所有节点。
nodes/
├── 127.0.0.1
│ ├── fisco-bcos
│ ├── node0
│ │ ├── conf
│ │ │ ├── ca.crt
│ │ │ ├── group.1.ini
│ │ │ ├── node.crt
│ │ │ ├── node.key
│ │ │ ├── node.nodeid
│ │ ├── config.ini
│ │ ├── start.sh
│ │ └── stop.sh
│ ├── node1
│ │.....
│ ├── node2
│ │.....
│ ├── node3
│ │.....
│ ├── sdk
│ │ ├── ca.crt
│ │ ├── node.crt
│ │ └── node.key
├── cert
│ ├── agency
│ │ ├── agency.crt
│ │ ├── agency.key
│ │ ├── agency.srl
│ │ ├── ca-agency.crt
│ │ ├── ca.crt
│ │ └── cert.cnf
│ ├── ca.crt
│ ├── ca.key
│ ├── ca.srl
│ └── cert.cnf
└── replace_all.sh
使用举例¶
单服务器单群组¶
构建本机上4节点的FISCO BCOS联盟连,使用默认起始端口30300,20200,8545
(4个节点会占用30300-30303
,20200-20203
,8545-8548
),监听外网Channel
和jsonrpc
端口允许外网通过SDK或API与节点交互。
# 下载最新预编译二进制
bash <(curl -s https://raw.githubusercontent.com/FISCO-BCOS/FISCO-BCOS/release-2.0.1/tools/ci/download_bin.sh) -b release-2.0.1
# 构建FISCO-BCOS联盟链
$ bash build_chain.sh -e bin/fisco-bcos -l "127.0.0.1:4" -i
# 生成成功后,输出`All completed`提示
Generating CA key...
==============================================================
Generating keys ...
Processing IP:127.0.0.1 Total:4 Agency:agency Groups:1
==============================================================
Generating configurations...
Processing IP:127.0.0.1 Total:4 Agency:agency Groups:1
==============================================================
[INFO] FISCO-BCOS Path : bin/fisco-bcos
[INFO] Start Port : 30300 20200 8545
[INFO] Server IP : 127.0.0.1:4
[INFO] State Type : storage
[INFO] RPC listen IP : 0.0.0.0
[INFO] Output Dir : /Users/fisco/WorkSpace/FISCO-BCOS/tools/nodes
[INFO] CA Key Path : /Users/fisco/WorkSpace/FISCO-BCOS/tools/nodes/cert/ca.key
==============================================================
[INFO] All completed. Files in /Users/fisco/WorkSpace/FISCO-BCOS/tools/nodes
多群组操作指南¶
本教程面向使用FISCO-BCOS多群组特性的用户,介绍如何通过build_chain
脚本创建多群组区块链节点以及如何通过控制台操作区块链。
创建多群组区块链¶
FISCO BCOS提供了一键安装脚本build_chain.sh
用于快速生成链配置文件。该脚本使用可参考快速建链指南。
创建下图所示拓扑的区块链系统,具体操作如下:
# 在home目录下创建节点目录
$ mkdir -p ~/fisco
$ cd ~/fisco
# 创建配置文件ipList
$ cat > ipList << EOF
192.168.0.1:2 agency1 1,2
192.168.0.2:2 agency2 1
192.168.0.3:2 agency3 2
EOF
# 获取二进制程序,并调用build_chain.sh脚本建链
# 若有源码编译FISCO BCOS需求,请参考 FISCO BCOS源码安装
$ bash <(curl -s https://raw.githubusercontent.com/FISCO-BCOS/FISCO-BCOS/release-2.0.1/tools/ci/download_bin.sh) -b release-2.0.1
# 下载build_chain.sh脚本
$ curl -LO https://raw.githubusercontent.com/FISCO-BCOS/FISCO-BCOS/release-2.0.1/tools/build_chain.sh && chmod u+x build_chain.sh
$ bash build_chain.sh -f ipList -e ./bin/fisco-bcos
# 拷贝节点到每台机器
$ scp -r nodes/192.168.0.1 192.168.0.1:~
$ scp -r nodes/192.168.0.2 192.168.0.2:~
$ scp -r nodes/192.168.0.3 192.168.0.3:~
# 登录每台机器,启动所有节点 && 查看节点进程 && 查看每个节点是否共识正常
$ ssh 192.168.0.1
$ cd ~/192.168.0.1 && bash start_all.sh && ps aux | grep fisco-bcos
# 有+++日志输出
$ tail -f nodes/log/* | grep +++
$ ssh 192.168.0.2
$ cd ~/192.168.0.2 && bash start_all.sh && ps aux | grep fisco-bcos
# 有+++日志输出
$ tail -f nodes/log/* | grep +++
$ ssh 192.168.0.3
$ cd ~/192.168.0.3 && bash start_all.sh && ps aux | grep fisco-bcos
# 有+++日志输出
$ tail -f nodes/log/* | grep +++
Important
- 若build_chain脚本执行失败,请查看nodes/build.log检查出错原因
- 若节点启动失败,请查看node*/nohup.out文件检查启动失败原因
- 节点启动失败可能的原因: 启动端口与其他应用端口重复
配置并启动控制台¶
为方便用户操作,FISCO BCOS在web3sdk基础上封装了控制台,用户可方便地通过控制台增删共识节点、修改区块可打包最大交易数、修改最大gas限制,并可调用FISCO BCOS底层提供的所有RPC接口,详细说明请参考控制台使用指南。
控制台是对web3sdk的封装,配置控制台前,需先部署web3sdk,详细配置可参考配置控制台
控制台配置完毕后,通过bash start group_id
启动组group_id
的控制台:
# 获取控制台
$ curl -LO https://github.com/FISCO-BCOS/LargeFiles/raw/master/tools/console.tar.gz && tar -zxf console.tar.gz
$ cd console
# 设启动组1的控制台
$ bash start 1
节点入网操作¶
为保证区块链安全性,FISCO BCOS引入了游离节点、观察者节点和共识节点,这三种节点类型可通过控制台相互转换,具体可参考节点入网。
控制台提供了 AddSealer(as) 、AddObserver(ao) 和 RemoveNode(rn) 三类命令将指定节点转换为共识节点、观察者节点和游离节点,并可使用 getSealerList(gsl)、getObserverList(gol) 和 getNodeIDList(gnl) 查看当前组的共识节点列表、观察者节点列表和组内所有节点列表。
例: 将指定节点分别转换成组1的共识节点、观察者节点、游离节点,具体操作和验证步骤如下:
Important
转换节点类型前,请确保节点Node ID存在,节点Node ID可在节点目录下执行 cat conf/node.nodeid获取
$ mkdir -p ~/fisco && cd ~/fisco
# 获取节点Node ID(设节点目录为~/nodes/192.168.0.1/node0/)
$ cat ~/fisco/nodes/192.168.0.1/node0/conf/node.nodeid
7a056eb611a43bae685efd86d4841bc65aefafbf20d8c8f6028031d67af27c36c5767c9c79cff201769ed80ff220b96953da63f92ae83554962dc2922aa0ef50
# 连接组1的控制台(设控制台位于~/console目录)
$ cd ~/fisco/console
$ bash start 1
# 将指定节点转换为共识节点
> addSealer 7a056eb611a43bae685efd86d4841bc65aefafbf20d8c8f6028031d67af27c36c5767c9c79cff201769ed80ff220b96953da63f92ae83554962dc2922aa0ef50
# 查询共识节点列表
> getSealerList
[
7a056eb611a43bae685efd86d4841bc65aefafbf20d8c8f6028031d67af27c36c5767c9c79cff201769ed80ff220b96953da63f92ae83554962dc2922aa0ef50
]
# 将指定节点转换为观察者节点
> addObserver 7a056eb611a43bae685efd86d4841bc65aefafbf20d8c8f6028031d67af27c36c5767c9c79cff201769ed80ff220b96953da63f92ae83554962dc2922aa0ef50
# 查询观察者节点列表
> getObserverList
[
7a056eb611a43bae685efd86d4841bc65aefafbf20d8c8f6028031d67af27c36c5767c9c79cff201769ed80ff220b96953da63f92ae83554962dc2922aa0ef50
]
# 将指定节点转换为游离节点
> removeNode 7a056eb611a43bae685efd86d4841bc65aefafbf20d8c8f6028031d67af27c36c5767c9c79cff201769ed80ff220b96953da63f92ae83554962dc2922aa0ef50
# 查询节点列表
> getNodeIDList
[
7a056eb611a43bae685efd86d4841bc65aefafbf20d8c8f6028031d67af27c36c5767c9c79cff201769ed80ff220b96953da63f92ae83554962dc2922aa0ef50
]
> getSealerList
[]
> getObserverList
[]
修改系统参数¶
FISCO BCOS系统目前主要包括如下系统参数(未来会扩展其他系统参数):
系统参数 | 默认值 | 含义 |
---|---|---|
tx_count_limit | 1000 | 一个区块中可打包的最大交易数目 |
tx_gas_limit | 300000000 | 一个区块最大gas限制 |
控制台提供 setSystemConfigByKey(ssc) 命令来修改这些系统参数,getSystemConfigByKey(gsc) 命令可查看系统参数的当前值:
Important
不建议随意修改tx_count_limit和tx_gas_limit,如下情况可修改这些参数:
- 机器网络或CPU等硬件性能有限:调小tx_count_limit,或降低业务压力;
- 业务逻辑太复杂,执行区块时gas不足:调大tx_gas_limit。
# 设置一个区块可打包最大交易数为500
> setSystemConfigByKey tx_count_limit 500
# 查询tx_count_limit
> getSystemConfigByKey tx_count_limit
[500]
# 设置区块gas限制为400000000
> getSystemConfigByKey tx_gas_limit 400000000
> getSystemConfigByKey
[400000000]
多群组使用案例¶
如下图,并行多组和星形组网拓扑是区块链应用中使用较广泛的两种组网方式。
- 并行多组:区块链中每个节点均属于多个群组,每个群组运行独立的应用;
- 星形拓扑:中心机构节点同时属于多个群组,运行多家机构应用,其他每家机构属于不同群组,运行各自应用。
下面以构建七节点星形拓扑和四节点并行多组区块链为例,详细介绍多群组操作方法。
安装依赖¶
部署FISCO BCOS区块链节点前,需安装openssl,leveldb,curl
等依赖软件,具体命令如下:
### CentOS
$ sudo yum -y install openssl openssl-devel leveldb leveldb-devel curl
### Ubuntu
$ sudo apt-get install openssl libssl-dev libleveldb-dev curl
星形拓扑¶
本章以介绍构建本机七节点三群组四机构的星形组网拓扑为例,介绍多群组使用方法。星型组网区块链详细组网情况如下:
本章以构建上图所示的星形拓扑区块链为例,介绍多群组使用方法。
星形区块链组网如下:
agencyA
:同时属于group1、group2、group3
,包括两个节点,节点IP均为127.0.0.1
;agencyB
:属于group1
,包括两个节点,节点IP均为127.0.0.1
;agencyC
:属于group2
,包括两个节点,节点IP均为127.0.0.1
;agencyD
:属于group3
,包括一个节点,节点IP均为127.0.0.1
。
Important
- 真实应用场景中,不建议将多个节点部署在同一台机器,建议根据 机器负载 选择部署节点数目
- 星形网络拓扑 中,核心节点(本例中agencyA节点)同属于所有群组,负载较大,建议单独部署在性能较好的机器
构建星形区块链¶
build_chain简单方便地支持任意拓扑多群组区块链构建,可使用该脚本构建星形拓扑区块链:
获取预编译二进制可执行程序
$ mkdir -p ~/fisco && cd ~/fisco
$ bash <(curl -s https://raw.githubusercontent.com/FISCO-BCOS/FISCO-BCOS/release-2.0.1/tools/ci/download_bin.sh) -b release-2.0.1
获取build_chain.sh脚本
$ mkdir -p ~/fisco && cd ~/fisco
$ curl -LO https://raw.githubusercontent.com/FISCO-BCOS/FISCO-BCOS/release-2.0.1/tools/build_chain.sh && chmod u+x build_chain.sh
生成星形区块链系统配置文件
$ mkdir -p ~/fisco && cd ~/fisco
# 生成区块链配置文件ip_list
$ cat > ip_list << EOF
127.0.0.1:2 agencyA 1,2,3
127.0.0.1:2 agencyB 1
127.0.0.1:2 agencyC 2
127.0.0.1:2 agencyD 3
EOF
# 查看配置文件ip_list内容
$ cat ip_list
127.0.0.1:2 agencyA 1,2,3
127.0.0.1:2 agencyB 1
127.0.0.1:2 agencyC 2
127.0.0.1:2 agencyD 3
使用build_chain脚本构建星形区块链
# 根据配置生成星形区块链
$ bash build_chain.sh -f ip_list -e ./bin/fisco-bcos
Generating CA key...
==============================================================
Generating keys ...
Processing IP:127.0.0.1 Total:2 Agency:agencyA Groups:1,2,3
Processing IP:127.0.0.1 Total:2 Agency:agencyB Groups:1
Processing IP:127.0.0.1 Total:2 Agency:agencyC Groups:2
Processing IP:127.0.0.1 Total:2 Agency:agencyD Groups:3
==============================================================
......此处省略其他输出......
==============================================================
[INFO] FISCO-BCOS Path : ./bin/fisco-bcos
[INFO] IP List File : ip_list
[INFO] Start Port : 30300 20200 8545
[INFO] Server IP : 127.0.0.1:2 127.0.0.1:2 127.0.0.1:2 127.0.0.1:2
[INFO] State Type : storage
[INFO] RPC listen IP : 127.0.0.1
[INFO] Output Dir : /home/fisco/nodes
[INFO] CA Key Path : /home/fisco/nodes/cert/ca.key
==============================================================
[INFO] All completed. Files in /home/fisco/nodes
# 生成的节点文件如下
$ tree
.
|-- 127.0.0.1
| |-- fisco-bcos
| |-- node0
| | |-- conf #节点配置目录
| | | |-- agency.crt
| | | |-- ca.crt
| | | |-- group.1.genesis
| | | |-- group.1.ini
| | | |-- group.2.genesis
| | | |-- group.2.ini
| | | |-- group.3.genesis
| | | |-- group.3.ini
| | | |-- node.crt
| | | |-- node.key
| | | `-- node.nodeid
| | |-- config.ini
| | |-- start.sh
| | `-- stop.sh
| |-- node1
| | |-- conf
......此处省略其他输出......
启动节点
节点提供start_all.sh
和stop_all.sh
脚本启动和停止节点。
# 进入节点目录
$ cd nodes/127.0.0.1
# 启动节点
$ bash start_all.sh
# 查看节点进程
$ ps aux |grep fisco-bcos
app 301 0.8 0.0 986644 7452 pts/0 Sl 15:21 0:00 /home/fisco/nodes/127.0.0.1/node5/../fisco-bcos -c config.ini
app 306 0.9 0.0 986644 6928 pts/0 Sl 15:21 0:00 /home/fisco/nodes/127.0.0.1/node6/../fisco-bcos -c config.ini
app 311 0.9 0.0 986644 7184 pts/0 Sl 15:21 0:00 /home/fisco/127.0.0.1/node7/../fisco-bcos -c config.ini
app 131048 2.1 0.0 1429036 7452 pts/0 Sl 15:21 0:00 /home/fisco/127.0.0.1/node0/../fisco-bcos -c config.ini
app 131053 2.1 0.0 1429032 7180 pts/0 Sl 15:21 0:00 /home/fisco/127.0.0.1/node1/../fisco-bcos -c config.ini
app 131058 0.8 0.0 986644 7928 pts/0 Sl 15:21 0:00 /home/fisco/127.0.0.1/node2/../fisco-bcos -c config.ini
app 131063 0.8 0.0 986644 7452 pts/0 Sl 15:21 0:00 /home/fisco/127.0.0.1/node3/../fisco-bcos -c config.ini
app 131068 0.8 0.0 986644 7672 pts/0 Sl 15:21 0:00 /home/fisco/127.0.0.1/node4/../fisco-bcos -c config.ini
查看群组共识状态
不发交易时,共识正常的节点会刷出+++
日志,本例中,node0、node1
同时属于group1、group2和group3
;node2、node3
属于group1
;node4、node5
属于group2
;node6、node7
属于group3
,可通过tail -f xxx.log | grep "g:${group_id}.*++"
查看各节点是否正常:
# 查看node0的group1是否正常共识
$ tail -f node0/log/* | grep "g:1.*++"
info|2019-02-11 15:33:09.914042| [g:1][p:264][CONSENSUS][SEALER]++++++++Generating seal on,blkNum=1,tx=0,myIdx=2,hash=72254a42....
# 查看node0的group2是否正常共识
$ tail -f node0/log/* | grep "g:2.*++"
info|2019-02-11 15:33:31.021697| [g:2][p:520][CONSENSUS][SEALER]++++++++Generating seal on,blkNum=1,tx=0,myIdx=3,hash=ef59cf17...
# 查看node0的group3是否正常共识
$ tail -f node0/log/* | grep "g:3.*++"
info|2019-02-11 15:33:51.022444| [g:3][p:776][CONSENSUS][SEALER]++++++++Generating seal on,blkNum=1,tx=0,myIdx=3,hash=2c455288...
# ... 查看node1的所有群组是否正常可参考node0操作方法...
# 查看node3的group1是否正常共识
$ tail -f node3/log/*| grep "g:1.*++"
info|2019-02-11 15:39:43.927167| [g:1][p:264][CONSENSUS][SEALER]++++++++Generating seal on,blkNum=1,tx=0,myIdx=3,hash=5e94bf63...
# 查看node5的group2是否正常共识
$ tail -f node5/log/* | grep "g:2.*++"
info|2019-02-11 15:39:42.922510| [g:2][p:520][CONSENSUS][SEALER]++++++++Generating seal on,blkNum=1,tx=0,myIdx=2,hash=b80a724d...
# 查看node6的group3是否正常共识
$ tail -f node6/log/* | grep "g:3.*++"
info|2019-02-11 15:39:58.994218| [g:3][p:776][CONSENSUS][SEALER]++++++++Generating seal on,blkNum=1,tx=0,myIdx=2,hash=eb5801bf...
向群组发交易¶
build_chain
提供了transTest.sh
脚本,可向指定群组发交易,该脚本使用方法:
$ bash transTest.sh ${交易数目} ${群组ID}
# ... 向group1发交易...
$ bash transTest.sh 10 1
Send transaction: 1
{"id":83,"jsonrpc":"2.0","result":"0x226e54480ce325a5858240a10864d7fc1127f2adc17e3e02dd314f91baab074b"}
Send transaction: 2
{"id":83,"jsonrpc":"2.0","result":"0x9f69fa21081ef18be04536de6583d8c633bf0387b15eb8b8aa9d7f6bbbd9654e"}
......此处省略其他输出......
# 查看出块情况
$ tail -f node0/log/* |grep "g:2.*Report"
info|2019-02-11 16:07:35.947676| [g:2][p:520][CONSENSUS][PBFT]^^^^^Report:,num=1,......此处省略其他输出......
# ...向group3发交易...
$ bash transTest.sh 10 3
Send transaction: 1
{"id":83,"jsonrpc":"2.0","result":"0x5f7a7c0a035a32a6fa17f0b797cc98eca45285f3e6347c6cd927efb7cd2a1a0b"}
Send transaction: 2
{"id":83,"jsonrpc":"2.0","result":"0x6f2279b37b98b79960e2e7291afbd89fceb9116c8d40859bf3d8374e2711b2dd"}
......此处省略其他输出......
# 查看出块情况
$ tail -f node0/log/* |grep "g:3.*Report"
info|2019-02-11 16:17:17.147941| [g:3][p:776][CONSENSUS][PBFT]^^^^^Report:,num=1,idx=3,hash=843f6498...,next=2,tx=1,myIdx=3
......此处省略其他输出......
节点加入/退出群组¶
通过控制台,FISCO BCOS可将指定节点加入到指定群组,也可将节点从指定群组删除,详细介绍请参考节点准入管理手册。控制台配置参考控制台操作手册。
Important
新节点加入群组前,请确保:
- 新加入节点正常共识: 正常共识的节点会刷出+++日志
- 群组内节点正常共识
将星形区块链系统中的node2加入group2为共识节点,具体操作方法如下:
启动控制台
$ mkidr -p ~/fisco
# 获取控制台
$ curl -LO https://github.com/FISCO-BCOS/LargeFiles/raw/master/tools/console.tar.gz
&& tar -zxf console.tar.gz
# 进入控制台操作目录
$ cd console
# 拷贝group2节点(node0, node1, node4, node5)的证书到控制台配置目录(以node0为例)
$ cp ~/fisco/nodes/127.0.0.1/node0/conf/node.* conf/
# 获取node0的channel_listen_port
$ cat ~/nodes/127.0.0.1/node4/config.ini | grep channel_listen_port
channel_listen_port=20200
# 参考控制台操作文档,
# 修改~/console/conf/applicationContext.xml的group id为2, ip:channel_listen_port为127.0.0.1:20200
# 确认Group ID:2
$ cat conf/applicationContext.xml | grep "groupId"
<property name="groupId" value="2" />
# 确认channel_listen_port
$ cat conf/applicationContext.xml | grep "[0-9].*:[0-9].*" | grep value
<value>127.0.0.1:20200</value>
# 启动web3sdk,连接group2所有节点
$ bash start 2
将node2加入group2为共识节点
# ...获取node2的node id...
$ cat node2/conf/node.nodeid
6dc585319e4cf7d73ede73819c6966ea4bed74aadbbcba1bbb777132f63d355965c3502bed7a04425d99cdcfb7694a1c133079e6d9b0ab080e3b874882b95ff4
# ... 从node0拷贝group2的配置到node2...
$ cp node0/conf/group.2.* node2/conf
# ...重启node2...
$ cd node2 && bash stop.sh && bash start.sh
# ...通过控制台将node2加入为共识节点
# 1. 查看当前共识节点列表
> getSealerList
[
9217e87c6b76184cf70a5a77930ad5886ea68aefbcce1909bdb799e45b520baa53d5bb9a5edddeab94751df179d54d41e6e5b83c338af0a19c0611200b830442,
227c600c2e52d8ec37aa9f8de8db016ddc1c8a30bb77ec7608b99ee2233480d4c06337d2461e24c26617b6fd53acfa6124ca23a8aa98cb090a675f9b40a9b106,
7a50b646fcd9ac7dd0b87299f79ccaa2a4b3af875bd0947221ba6dec1c1ba4add7f7f690c95cf3e796296cf4adc989f4c7ae7c8a37f4505229922fb6df13bb9e,
8b2c4204982d2a2937261e648c20fe80d256dfb47bda27b420e76697897b0b0ebb42c140b4e8bf0f27dfee64c946039739467b073cf60d923a12c4f96d1c7da6
]
# 2. 将node2加入到共识节点
> addSealer 6dc585319e4cf7d73ede73819c6966ea4bed74aadbbcba1bbb777132f63d355965c3502bed7a04425d99cdcfb7694a1c133079e6d9b0ab080e3b874882b95ff4
{
"code":1,
"msg":"success"
}
# 3. 查看共识节点列表
> getSealerList
[
9217e87c6b76184cf70a5a77930ad5886ea68aefbcce1909bdb799e45b520baa53d5bb9a5edddeab94751df179d54d41e6e5b83c338af0a19c0611200b830442,
227c600c2e52d8ec37aa9f8de8db016ddc1c8a30bb77ec7608b99ee2233480d4c06337d2461e24c26617b6fd53acfa6124ca23a8aa98cb090a675f9b40a9b106,
7a50b646fcd9ac7dd0b87299f79ccaa2a4b3af875bd0947221ba6dec1c1ba4add7f7f690c95cf3e796296cf4adc989f4c7ae7c8a37f4505229922fb6df13bb9e,
8b2c4204982d2a2937261e648c20fe80d256dfb47bda27b420e76697897b0b0ebb42c140b4e8bf0f27dfee64c946039739467b073cf60d923a12c4f96d1c7da6,
6dc585319e4cf7d73ede73819c6966ea4bed74aadbbcba1bbb777132f63d355965c3502bed7a04425d99cdcfb7694a1c133079e6d9b0ab080e3b874882b95ff4 # 新加入节点
]
查看新加入节点出块情况
通过以上操作可看出,node2已成功加入group2。
通过tail -f node2/log/* | grep "g:2.*++"
查看node2的group2是否出块正常:
# 查看节点共识情况
$ tail -f node2/log/* | grep "g:2.*++"
info|2019-02-11 18:41:31.625599| [g:2][p:520][CONSENSUS][SEALER]++++++++Generating seal on,blkNum=9,tx=0,myIdx=1,hash=c8a1ed9c...
......此处省略其他输出......
向group2发交易,查看node2的group2共识情况:
$ bash transTest.sh 10 2
Send transaction: 1
{"id":83,"jsonrpc":"2.0","result":"0xd834f909861599c475fa0a04b4afa4759b20fdaea683dd9692bbf79541f16b01"}
Send transaction: 2
......此处省略其他输出......
# 查看node2节点group2的出块情况
$ tail -f node2/log/* | grep "g:2.*Report"
info|2019-02-11 18:53:20.708366| [g:2][p:520][CONSENSUS][PBFT]^^^^^Report:,num=9,idx=3,hash=80c98d31...,next=10,tx=1,myIdx=1
......此处省略其他输出......
并行多组¶
并行多组区块链搭建方法与星形拓扑区块链搭建方法类似,以搭建四节点两群组并行多链系统为例:
- 群组1:包括四个节点,节点IP均为
127.0.0.1
; - 群组2: 包括四个节点,节点IP均为
127.0.0.1
。
Important
- 真实应用场景中,不建议将多个节点部署在同一台机器 ,建议根据 机器负载 选择部署节点数目
- 为演示并行多组扩容流程,这里仅先创建group1
- 并行多组场景中,节点加入和退出群组操作与星形组网拓扑类似
构建单群组四节点区块链¶
用build_chain脚本生成单群组四节点区块链
$ mkdir -p ~/fisco && cd ~/fisco
# 获取fisco-bcos二进制文件
$ bash <(curl -s https://raw.githubusercontent.com/FISCO-BCOS/FISCO-BCOS/release-2.0.1/tools/ci/download_bin.sh)
# 获取build_chain.sh脚本
$ curl -LO https://raw.githubusercontent.com/FISCO-BCOS/FISCO-BCOS/release-2.0.1/tools/build_chain.sh && chmod u+x build_chain.sh
# 构建本机单群组四节点区块链(生产环境中,建议节点部署在不同物理机)
$ bash build_chain.sh -l "127.0.0.1:4" -e bin/fisco-bcos -o multi_nodes
Generating CA key...
==============================================================
Generating keys ...
Processing IP:127.0.0.1 Total:4 Agency:agency Groups:1
==============================================================
Generating configurations...
Processing IP:127.0.0.1 Total:4 Agency:agency Groups:1
==============================================================
[INFO] FISCO-BCOS Path : bin/fisco-bcos
[INFO] Start Port : 30300 20200 8545
[INFO] Server IP : 127.0.0.1:4
[INFO] State Type : storage
[INFO] RPC listen IP : 127.0.0.1
[INFO] Output Dir : /home/fisco/multi_nodes
[INFO] CA Key Path : /home/fisco/multi_nodes/cert/ca.key
==============================================================
[INFO] All completed. Files in /home/fisco/multi_nodes
启动所有节点
# 进入节点目录
$ cd ~/fisco/multi_nodes/127.0.0.1
$ bash start_all.sh
# 查看进程情况
$ ps aux | grep fisco-bcos
app 55028 0.9 0.0 986384 6624 pts/2 Sl 20:59 0:00 /home/fisco/multi_nodes/127.0.0.1/node0/../fisco-bcos -c config.ini
app 55034 0.8 0.0 986104 6872 pts/2 Sl 20:59 0:00 /home/fisco/multi_nodes/127.0.0.1/node1/../fisco-bcos -c config.ini
app 55041 0.8 0.0 986384 6584 pts/2 Sl 20:59 0:00 /home/fisco/multi_nodes/127.0.0.1/node2/../fisco-bcos -c config.ini
app 55047 0.8 0.0 986396 6656 pts/2 Sl 20:59 0:00 /home/fisco/multi_nodes/127.0.0.1/node3/../fisco-bcos -c config.ini
查看节点共识情况
# 查看node0共识情况
$ tail -f node0/log/* | grep "g:1.*++"
info|2019-02-11 20:59:52.065958| [g:1][p:264][CONSENSUS][SEALER]++++++++Generating seal on,blkNum=1,tx=0,myIdx=2,hash=da72649e...
# 查看node1共识情况
$ tail -f node1/log/* | grep "g:1.*++"
info|2019-02-11 20:59:54.070297| [g:1][p:264][CONSENSUS][SEALER]++++++++Generating seal on,blkNum=1,tx=0,myIdx=0,hash=11c9354d...
# 查看node2共识情况
$ tail -f node2/log/* | grep "g:1.*++"
info|2019-02-11 20:59:55.073124| [g:1][p:264][CONSENSUS][SEALER]++++++++Generating seal on,blkNum=1,tx=0,myIdx=1,hash=b65cbac8...
# 查看node3共识情况
$ tail -f node3/log/* | grep "g:1.*++"
info|2019-02-11 20:59:53.067702| [g:1][p:264][CONSENSUS][SEALER]++++++++Generating seal on,blkNum=1,tx=0,myIdx=3,hash=0467e5c4...
将group2加入区块链¶
并行多组区块链每个群组的genesis
配置文件几乎相同,但[group].index不同,为群组号。
生成新群组配置
# 拷贝group1的配置
$ cp node0/conf/group.1.genesis group.2.genesis
# 修改群组ID
$ vim group.2.genesis
[group]
index=2
# 将配置拷贝到各个节点
$ cp group.2.genesis node0/conf
$ cp group.2.genesis node1/conf
$ cp group.2.genesis node2/conf
$ cp group.2.genesis node3/conf
# 重启区块链
$ bash stop.sh
$ bash start.sh
查看群组共识情况¶
# 查看node0 group2共识情况
$ tail -f node0/log/* | grep "g:2.*++"
info|2019-02-11 21:13:28.541596| [g:2][p:520][CONSENSUS][SEALER]++++++++Generating seal on,blkNum=1,tx=0,myIdx=2,hash=f3562664...
# 查看node1 group2共识情况
$ tail -f node1/log/* | grep "g:2.*++"
info|2019-02-11 21:13:30.546011| [g:2][p:520][CONSENSUS][SEALER]++++++++Generating seal on,blkNum=1,tx=0,myIdx=0,hash=4b17e74f...
# 查看node2 group2共识情况
$ tail -f node2/log/* | grep "g:2.*++"
info|2019-02-11 21:13:59.653615| [g:2][p:520][CONSENSUS][SEALER]++++++++Generating seal on,blkNum=1,tx=0,myIdx=1,hash=90cbd225...
# 查看node3 group2共识情况
$ tail -f node3/log/* | grep "g:2.*++"
info|2019-02-11 21:14:01.657428| [g:2][p:520][CONSENSUS][SEALER]++++++++Generating seal on,blkNum=1,tx=0,myIdx=3,hash=d7dcb462...
向群组发交易¶
使用transTest.sh
脚本向group1
和group2
发交易,验证区块链网络是否正常:
# ...向group1发交易,并查看节点共识情况...
bash transTest.sh 10 1
Send transaction: 1
{"id":83,"jsonrpc":"2.0","result":"0x24827ef7b0bed013123d9981ff61ba0a1761747a475e187467e70d9ff20c0714"}
Send transaction: 2
{"id":83,"jsonrpc":"2.0","result":"0x42058b83924248adbedf2e8f65e60b0986d378ee5255b89fd72cd76f5b0d07bf"}
......此处省略其他输出......
# 查看节点出块情况
$ tail -f node0/log/log_2019021121.12.log | grep "g:1.*Report"
info|2019-02-11 21:14:57.216548| [g:1][p:264][CONSENSUS][PBFT]^^^^^Report:,num=1,idx=3,hash=be961c98...,next=2,tx=1,myIdx=2
......此处省略其他输出......
# ...向group2发交易,并查看节点出块情况...
bash transTest.sh 10 2
Send transaction: 1
{"id":83,"jsonrpc":"2.0","result":"0xf8278a38d9243bfc6c7c146cb222fe13a7f8436b7df169bd1a90f7e17954c6a6"}
Send transaction: 2
{"id":83,"jsonrpc":"2.0","result":"0x43dca46e44c7658bb86db15d555081869e0157bd8ff7bc901fb3f648ead13d8e"}
......此处省略其他输出......
# 查看节点出块情况
$ tail -f node0/log/log_2019021121.12.log | grep "g:2.*Report"
info|2019-02-11 21:15:25.310565| [g:2][p:520][CONSENSUS][PBFT]^^^^^Report:,num=1,idx=3,hash=5d006230...,next=2,tx=1,myIdx=2
......此处省略其他输出......
配置文件与配置项¶
FISCO BCOS支持多账本,每条链包括多个独立账本,账本间数据相互隔离,群组间交易处理相互隔离,每条链主要包括一个总体配置config.ini
和各个账本配置group.group_id.genesis
、group.group_id.ini
。
config.ini
:主配置文件,主要配置RPC、P2P、SSL证书、账本配置文件路径等信息;group.group_id.genesis
:群组不可变配置文件,须保证群组内所有节点该配置一致,节点启动后,不可更改该配置,主要配置群组共识算法、存储类型、最大gas限制等系统信息;group.group_id.ini
:群组可变配置文件,包括交易池大小等,配置后重启节点生效。
PS : 由于多群组共享网络带宽、CPU和内存资源,因此为了保证服务的稳定性,一台机器上不推荐配置过多群组。
下表是单群组单节点推荐的配置,节点耗费资源与群组个数呈线性关系,您可根据实际的业务需求和机器资源,合理地配置群组数目:
配置 | 最低配置 | 推荐配置 |
---|---|---|
CPU | 1.5GHz | 2.4GHz |
内存 | 1GB | 8GB |
核心 | 1核 | 4核 |
带宽 | 1Mb | 10Mb |
主配置文件config.ini¶
config.ini
采用ini
格式,主要包括 rpc、p2p、group、secure和log 配置项。
Important
网络配置须知:
- 云主机的公网ip均为虚拟ip,若listen_ip填写外网IP,会绑定失败,须填写0.0.0.0
- RPC/P2P/Channel监听端口必须位于1024-65535范围内,且不能与机器上其他应用监听端口冲突
配置RPC¶
若为127.0.0.1。。。 改为出于安全考虑,建链脚本默认监听127.0.0.1,如果需要外网访问RPC或外网使用SDK请监听。。。
listen_ip
: 安全考虑,建链脚本默认监听127.0.0.1,如果需要外网访问RPC或外网使用SDK请监听外网IP
或0.0.0.0
;channel_listen_port
: Channel监听端口,对应到SDK配置中的channel_listen_port
;jsonrpc_listen_port
: RPC监听端口。
RPC配置示例如下:
[rpc]
;rpc listen ip
listen_ip=127.0.0.1
;channelserver listen port
channel_listen_port=30301
;rpc listen port
jsonrpc_listen_port=30302
配置P2P¶
当前版本FISCO BCOS必须在config.ini
配置中配置连接节点的IP
和Port
,P2P相关配置包括:
listen_ip
:P2P监听端口,若为127.0.0.1,则仅监听本机RPC请求,为0.0.0.0和内网ip时,监听所有请求;listen_port
:节点P2P监听端口;node.*
: 节点需连接的所有节点ip:port
。
P2P配置示例如下:
[p2p]
;p2p listen ip
listen_ip=0.0.0.0
;p2p listen port
listen_port=30300
;nodes to connect
node.0=127.0.0.1:30300
node.1=127.0.0.1:30304
node.2=127.0.0.1:30308
node.3=127.0.0.1:30312
配置账本文件路径¶
[group]
配置本节点所属的所有群组配置路径:
group_data_path
: 群组数据存储路径;group_config_path
: 群组配置文件路径。
节点根据group_config_path
路径下的所有.genesis
后缀文件启动群组
;group configurations
;WARNING: group 0 is forbided
[group]
;所有群组数据放置于节点的data子目录
group_data_path=data/
;程序自动加载该路径下的所有.genesis文件
group_config_path=conf/
配置证书信息¶
基于安全考虑,FISCO BCOS节点间采用SSL加密通信,[secure]
配置SSL连接的证书信息:
data_path
:证书和私钥文件所在目录;key
:节点私钥相对于data_path
的路径;cert
: 证书node.crt
相对于data_path
的路径;ca_cert
: ca证书路径。
;certificate configuration
[secure]
;directory the certificates located in
data_path=conf/
;the node private key file
key=node.key
;the node certificate file
cert=node.crt
;the ca certificate file
ca_cert=ca.crt
配置黑名单列表¶
基于防作恶考虑,FISCO BCOS允许节点配置不受信任的节点黑名单列表,拒绝与这些黑名单节点建立连接,通过[crl]
配置:
crl.idx
: 黑名单节点的Node ID, 节点Node ID可通过node.nodeid
文件获取;idx
是黑名单节点的索引。
黑名单的详细信息还可参考CA黑名单
黑名单列表配置示例如下:
;certificate rejected list
[crl]
crl.0=4d9752efbb1de1253d1d463a934d34230398e787b3112805728525ed5b9d2ba29e4ad92c6fcde5156ede8baa5aca372a209f94dc8f283c8a4fa63e
3787c338a4
配置日志信息¶
FISCO BCOS支持轻量级的easylogging++,也支持功能强大的boostlog,可通过编译开关配置使用这两种日志,FISCO BCOS默认使用boostlog,详细可参考日志操作手册。
level
: 日志级别,当前主要包括trace、debug、info、warning、error
五种日志级别,设置某种日志级别后,日志文件中会输大于等于该级别的日志,日志级别从大到小排序error > warning > info > debug > trace
;max_log_file_size
:每个日志文件最大容量;flush
:boostlog默认开启日志自动刷新,若需提升系统性能,建议将该值设置为false。
boostlog示例配置如下:
;log configurations
[log]
;the directory of the log
log_path=./log
;log level INFO DEBUG TRACE
level=info
max_log_file_size=209715200
flush=true
配置easylogging++¶
为了尽量减少配置文件,FISCO BCOS将easyloggin++的配置信息都集中到了config.ini的[log]
配置,一般建议不手动更改除了日志级别外的其他配置,开启easylogging++的方法可参考启用easylogging++。
format
:全局日志格式;log_flush_threshold
:日志刷新频率设置,即每log_flush_threshold
行刷新日志到文件一次。
easylogging++示例配置如下:
;log configurations
[log]
;the directory of the log
log_path=./log
;log level INFO DEBUG TRACE
level=info
max_log_file_size=209715200
;easylog config
format=%level|%datetime{%Y-%M-%d %H:%m:%s:%g}|%msg
log_flush_threshold=100
可选配置:落盘加密¶
为了保障节点数据机密性,FISCO BCOS引入落盘加密保障节点数据的机密性,落盘加密操作手册请参考这里。
config.ini
中的data_secure
用于配置落盘加密,主要包括(落盘加密具体操作请参考操作手册):
enable
: 是否开启落盘加密,默认不开启;keycenter_ip
:Key Center服务的部署IP;keycenter_port
:Key Center服务的监听端口;cipher_data_key
: 节点数据加密密钥的密文,cipher_data_key
的产生参考落盘加密操作手册。
落盘加密节点配置示例如下:
[data_secure]
enable=true
keycenter_ip=127.0.0.1
keycenter_port=31443
cipher_data_key=ed157f4588b86d61a2e1745efe71e6ea
群组不可变配置说明¶
每个群组都有单独的配置文件,按照启动后是否可更改,可分为群组不可变配置和群组可变配置。
群组不可变配置一般位于节点的conf
目录下.genesis
后缀配置文件中。
如:group1
的不可变配置一般命名为group.1.genesis
,群组不可变配置主要包括群组ID、共识、存储和gas相关的配置。
Important
配置不可变配置时,需注意:
- 配置群组内一致 :群组不可变配置用于产生创世块(第0块),因此必须保证群组内所有节点的该配置一致
- 节点启动后不可更改 :不可变配置已经作为创世块写入了系统表,链初始化后不可更改
- 链初始化后,即使更改了genesis配置,新的配置不会生效,系统仍然使用初始化链时的genesis配置
- 由于genesis配置要求群组内所有节点一致,建议使用 build_chain 生成该配置
共识配置¶
[consensus]
涉及共识相关配置,包括:
consensus_type
:共识算法类型,目前支持PBFT和Raft,默认使用PBFT共识算法;max_trans_num
:一个区块可打包的最大交易数,默认是1000,链初始化后,可通过控制台动态调整该参数;node.idx
:共识节点列表,配置了参与共识节点的Node ID,节点的Node ID可通过${data_path}/node.nodeid文件获取(其中${data_path}可通过主配置config.ini
的[secure].data_path
配置项获取)
;consensus configuration
[consensus]
;consensus algorithm type, now support PBFT(consensus_type=pbft) and Raft(consensus_type=raft)
consensus_type=pbft
;the max number of transactions of a block
max_trans_num=1000
;the node id of leaders
node.0=123d24a998b54b31f7602972b83d899b5176add03369395e53a5f60c303acb719ec0718ef1ed51feb7e9cf4836f266553df44a1cae5651bc6ddf50
e01789233a
node.1=70ee8e4bf85eccda9529a8daf5689410ff771ec72fc4322c431d67689efbd6fbd474cb7dc7435f63fa592b98f22b13b2ad3fb416d136878369eb41
3494db8776
node.2=7a056eb611a43bae685efd86d4841bc65aefafbf20d8c8f6028031d67af27c36c5767c9c79cff201769ed80ff220b96953da63f92ae83554962dc2
922aa0ef50
node.3=fd6e0bfe509078e273c0b3e23639374f0552b512c2bea1b2d3743012b7fed8a9dec7b47c57090fa6dcc5341922c32b89611eb9d967dba5f5d07be7
4a5aed2b4a
存储模块配置¶
存储主要包括state和AMDB,state
涉及交易状态存储,AMDB
存储涉及系统表,分别在[storage]
和[state]
中配置:
[storage].type
:存储的DB类型,目前仅支持LevelDB,后续会支持Mysql;[state].type
:state类型,目前支持Storage state和MPT state,默认为Storage state,Storage state将交易执行结果存储在系统表中,效率较高,MPT state将交易执行结果存储在MPT树中,效率较低,但包含完整的历史信息。
Important
推荐使用 Storage state ,除有特殊需求,不建议使用MPT State
[storage]
;storage db type, now support leveldb
type=LevelDB
[state]
;support storag/mpt
type=storage
gas配置¶
FISCO BCOS兼容以太坊虚拟机(evm),为了防止针对evm的DOS攻击,evm在执行交易时,引入了gas概念,用来度量智能合约执行过程中消耗的计算和存储资源,包括交易最大gas限制和区块最大gas限制,若交易或区块执行消耗的gas超过限制(gas limit),则丢弃交易或区块。FISCO BCOS是联盟链,简化了gas设计,仅保留交易最大gas限制,区块最大gas通过共识配置的max_trans_num和交易最大gas限制一起约束。FISCO BCOS通过genesis的[tx].gas_limit来配置交易最大gas限制,默认是300000000,链初始化完毕后,可通过控制台指令动态调整gas限制。
;tx gas limit
[tx]
gas_limit=300000000
账本可变配置说明¶
账本可变配置位于节点conf
目录下.ini
后缀的文件中。
如:group1
可变配置一般命名为group.1.ini
,可变配置主要包括交易池大小、共识消息转发的TTL。
交易池配置¶
FISCO-BCOS将交易池容量配置开放给用户,用户可根据自己的业务规模需求、稳定性需求以及节点的硬件配置动态调整交易池大小。
交易池配置示例如下:
;txpool limit
[tx_pool]
limit=10000
共识配置¶
PBFT
共识算法为了保证共识过程最大网络容错性,每个共识节点收到有效的共识消息后,会向其他节点广播该消息,在网络较好的环境下,共识消息转发机制会造成额外的网络带宽浪费,因此在群组可变配置项中引入了TTL
来控制消息最大转发次数,消息最大转发次数为TTL-1
,该配置项仅对PBFT有效。
设置共识消息最多转发一次,配置示例如下:
; the ttl for broadcasting pbft message
[consensus]
ttl=2
控制台手册¶
控制台是FISCO BCOS 2.0重要的交互式客户端工具,它通过SDK与区块链节点建立连接,实现对区块链节点数据的读写访问请求。控制台拥有丰富的命令,包括查询区块链状态、管理区块链节点、部署并调用合约等功能。
控制台命令¶
控制台命令由两部分组成,即指令和指令相关的参数:
- 指令: 指令是执行的操作命令,包括查询区块链相关信息,部署合约和调用合约的指令等。其中部分指令调用RPC接口,因此与RPC接口同名;但同时为了输入简洁,提供对应的缩写指令。
- 指令相关的参数: 指令调用接口需要的参数,指令与参数以及参数与参数之间均用空格分隔。其中字符串参数需要加上双引号,字符串不能带空格。与RPC接口同名命令的输入参数和获取信息字段的详细解释参考RPC接口。
控制台响应¶
当发起一个控制台命令时,控制台会获取命令执行的结果,并且在终端展示执行结果,执行结果分为2类:
- 正确结果: 命令返回正确的执行结果,以字符串或是json的形式返回。
- 错误结果: 命令返回错误的执行结果,以字符串或是json的形式返回。
注:
- 控制台的命令调用RPC接口时,当RPC返回错误响应(具体错误码见RPC设计文档),将以json格式显示错误响应的error字段信息。
- 命令操作系统功能时,会返回json字段,其中code是返回码,msg是返回码的描述信息。响应分为三类:
- 操作成功响应:code大于等于0表示操作成功,其code值为成功操作的记录数,msg为“success”。
- 系统性错误响应:无权限操作,其code为-1, msg是“non-authorized”。
- 逻辑性错误响应:定义如下。
code | msg |
---|---|
-30 | table name and address exist |
-31 | table name and address does not exist |
-40 | invalid nodeID |
-41 | last sealer cannot be removed |
-42 | nodeID is not in network |
-43 | nodeID is not in group peers |
-44 | nodeID is already in sealer list |
-45 | nodeID is already in observer list |
-50 | address and version exist |
-51 | version exceeds maximum(40) length |
-60 | set invalid configuration values |
控制台配置与运行¶
配置运行¶
搭建FISCO BCOS区块链请参考建链脚本。
控制台配置¶
获取控制台
curl -LO https://github.com/FISCO-BCOS/LargeFiles/raw/master/tools/console.tar.gz
tar -zxf console.tar.gz
配置控制台:
- 区块链节点和证书的配置:
- 配置conf目录下的applicationContext.xml文件,配置如下图所示,其中红框标记的内容根据区块链节点配置做相应修改。
配置项详细说明如下:
- encryptType: 国密算法开关(默认为0)
- 0: 不使用国密算法发交易
- 1: 使用国密算法发交易(开启国密功能,需要连接的区块链节点是国密节点,搭建国密版FISCO BCOS区块链参考这里)。
- groupChannelConnectionsConfig:
- 配置待连接的群组,可以配置一个或多个群组,每个群组需要配置群组ID
- 每个群组可以配置一个或多个节点,设置群组节点的配置文件config.ini中
[rpc]
部分的listen_ip
和channel_listen_port
。
- channelService: 通过指定群组ID配置SDK实际连接的群组,指定的群组ID是groupChannelConnectionsConfig配置中的群组ID。SDK将与群组中配置的节点均建立连接,然后随机选择一个节点发送请求。
- encryptType: 国密算法开关(默认为0)
Important
控制台配置说明
- 说明: 当控制台配置文件在一个群组内配置多个节点连接时,由于群组内的某些节点在操作过程中可能退出群组,因此控制台轮询节点查询时,其返回信息可能不一致,属于正常现象。建议使用控制台时,配置一个节点或者保证配置的节点始终在群组中,这样在同步时间内查询的群组内信息保持一致。
启动控制台¶
$ bash start
# 输出下述信息表明启动成功
=====================================================================================
Welcome to FISCO BCOS console!
Type 'help' or 'h' for help. Type 'quit' or 'q' to quit console.
________ ______ ______ ______ ______ _______ ______ ______ ______
| | \/ \ / \ / \ | \ / \ / \ / \
| $$$$$$$$\$$$$$| $$$$$$| $$$$$$| $$$$$$\ | $$$$$$$| $$$$$$| $$$$$$| $$$$$$\
| $$__ | $$ | $$___\$| $$ \$| $$ | $$ | $$__/ $| $$ \$| $$ | $| $$___\$$
| $$ \ | $$ \$$ \| $$ | $$ | $$ | $$ $| $$ | $$ | $$\$$ \
| $$$$$ | $$ _\$$$$$$| $$ __| $$ | $$ | $$$$$$$| $$ __| $$ | $$_\$$$$$$\
| $$ _| $$_| \__| $| $$__/ | $$__/ $$ | $$__/ $| $$__/ | $$__/ $| \__| $$
| $$ | $$ \\$$ $$\$$ $$\$$ $$ | $$ $$\$$ $$\$$ $$\$$ $$
\$$ \$$$$$$ \$$$$$$ \$$$$$$ \$$$$$$ \$$$$$$$ \$$$$$$ \$$$$$$ \$$$$$$
=====================================================================================
启动脚本说明¶
$ bash start [groupID] [privateKey]
启动命令可以指定两个可选参数:
groupId
: - 群组ID, 不指定则默认为群组1。privateKey
: - 交易发送者外部账号的私钥,不指定则默认从conf目录下的privateKey.properties中读取私钥,如果该文件内容被清空,则随机生成外部账号私钥并将生产的私钥保持在该私钥配置文件中。
示例
# 以群组2,私钥账号地址为3bed914595c159cbce70ec5fb6aff3d6797e0c5ee5a7a9224a21cae8932d84a4登录控制台
$ bash start 2 3bed914595c159cbce70ec5fb6aff3d6797e0c5ee5a7a9224a21cae8932d84a4
控制台命令¶
help¶
输入help或h,查看控制台所有的命令。
> help
-------------------------------------------------------------------------------------
help(h) Provide help information.
getBlockNumber(gbn) Query the number of most recent block.
getPbftView(gpv) Query the pbft view of node.
getSealerList(gsl) Query nodeID list for sealer nodes.
getObserverList(gol) Query nodeID list for observer nodes.
getNodeIDList(gnl) Query nodeID list for all connected nodes.
getGroupPeers(ggp) Query nodeID list for sealer and observer nodes.
getPeers(gps) Query peers currently connected to the client.
getConsensusStatus(gcs) Query consensus status.
getSyncStatus(gss) Query sync status.
getClientVersion(gcv) Query the current client version.
getGroupList(ggl) Query group list.
getBlockByHash(gbbh) Query information about a block by hash.
getBlockByNumber(gbbn) Query information about a block by block number.
getBlockHashByNumber(ghbn) Query block hash by block number.
getTransactionByHash(gtbh) Query information about a transaction requested by transaction hash.
getTransactionByBlockHashAndIndex(gthi) Query information about a transaction by block hash and transaction index position.
getTransactionByBlockNumberAndIndex(gtni) Query information about a transaction by block number and transaction index position.
getTransactionReceipt(gtr) Query the receipt of a transaction by transaction hash.
getPendingTransactions(gpt) Query pending transactions.
getPendingTxSize(gpts) Query pending transactions size.
getCode(gc) Query code at a given address.
getTotalTransactionCount(gtc) Query total transaction count.
deploy(d) Deploy a contract on blockchain.
call(c) Call a contract by a function and paramters.
deployByCNS(dbc) Deploy a contract on blockchain by CNS.
callByCNS(cbc) Call a contract by a function and paramters by CNS.
queryCNS(qcs) Query cns information by contract name and contract version.
addSealer(as) Add a sealer node.
addObserver(ao) Add an observer node.
removeNode(rn) Remove a node.
setSystemConfigByKey(ssc) Set a system config.
getSystemConfigByKey(gsc) Query a system config value by key.
addUserTableManager(aum) Add authority for user table by table name and address.
removeUserTableManager(rum) Remove authority for user table by table name and address.
queryUserTableManager(qum) Query authority for user table information.
addDeployAndCreateManager(adm) Add authority for deploy contract and create user table by address.
removeDeployAndCreateManager(rdm) Remove authority for deploy contract and create user table by address.
queryDeployAndCreateManager(qdm) Query authority information for deploy contract and create user table.
addAuthorityManager(aam) Add authority for authority configuration by address.
removeAuthorityManager(ram) Remove authority for authority configuration by address.
queryAuthorityManager(qam) Query authority information for authority configuration.
addNodeManager(anm) Add authority for node configuration by address.
removeNodeManager(rnm) Remove authority for node configuration by address.
queryNodeManager(qnm) Query authority information for node configuration.
addCNSManager(acm) Add authority for CNS by address.
removeCNSManager(rcm) Remove authority for CNS by address.
queryCNSManager(qcm) Query authority information for CNS.
addSysConfigManager(asm) Add authority for system configuration by address.
removeSysConfigManager(rsm) Remove authority for system configuration by address.
querySysConfigManager(qsm) Query authority information for system configuration.
quit(q) Quit console.
-------------------------------------------------------------------------------------
注:
- help显示每条命令的含义是:命令全名(缩写名) 命令功能描述
- 查看具体命令的使用介绍说明,输入命令全名或缩写名 -h或–help查看。例如:
> getBlockByNumber -h
Query information about a block by block number.
Usage: gbbn blockNumber [boolean]
blockNumber -- Integer of a block number.
boolean -- (optional) If true it returns the full transaction objects, if false only the hashes of the transactions.
> gbbh -h
Query information about a block by block number.
Usage: gbbn blockNumber [boolean]
blockNumber -- Integer of a block number.
boolean -- (optional) If true it returns the full transaction objects, if false only the hashes of the transactions.
getSealerList¶
运行getSealerList或gsl,查看共识节点列表。
> gsl
[
0c0bbd25152d40969d3d3cee3431fa28287e07cff2330df3258782d3008b876d146ddab97eab42796495bfbb281591febc2a0069dcc7dfe88c8831801c5b5801,
10b3a2d4b775ec7f3c2c9e8dc97fa52beb8caab9c34d026db9b95a72ac1d1c1ad551c67c2b7fdc34177857eada75836e69016d1f356c676a6e8b15c45fc9bc34,
622af37b2bd29c60ae8f15d467b67c0a7fe5eb3e5c63fdc27a0ee8066707a25afa3aa0eb5a3b802d3a8e5e26de9d5af33806664554241a3de9385d3b448bcd73
]
getObserverList¶
运行getObserverList或gol,查看观察节点列表。
> gol
[
037c255c06161711b6234b8c0960a6979ef039374ccc8b723afea2107cba3432dbbc837a714b7da20111f74d5a24e91925c773a72158fa066f586055379a1772
]
getNodeIDList¶
运行getNodeIDList或gnl,查看节点及连接p2p节点的nodeID列表。
> gnl
[
41285429582cbfe6eed501806391d2825894b3696f801e945176c7eb2379a1ecf03b36b027d72f480e89d15bacd43462d87efd09fb0549e0897f850f9eca82ba,
87774114e4a496c68f2482b30d221fa2f7b5278876da72f3d0a75695b81e2591c1939fc0d3fadb15cc359c997bafc9ea6fc37345346acaf40b6042b5831c97e1,
29c34347a190c1ec0c4507c6eed6a5bcd4d7a8f9f54ef26da616e81185c0af11a8cea4eacb74cf6f61820292b24bc5d9e426af24beda06fbd71c217960c0dff0,
d5b3a9782c6aca271c9642aea391415d8b258e3a6d92082e59cc5b813ca123745440792ae0b29f4962df568f8ad58b75fc7cea495684988e26803c9c5198f3f8
]
getConsensusStatus¶
运行getConsensusStatus或gcs,查看共识状态。
> gcs
[
{
"accountType":1,
"allowFutureBlocks":true,
"cfgErr":false,
"connectedNodes":3,
"consensusedBlockNumber":91,
"currentView":2834,
"f":1,
"highestblockHash":"77e5b6d799edabaeae654ac5cea9baacd6f8e7ace33531d40c7ed65192de1f02",
"highestblockNumber":90,
"leaderFailed":false,
"sealer.0":"3106a6310b5edc07658d09cf6a96ba597a5cfc8fbec8587c6786112808286c11f2bc81db9133328983bc641f1c97ce38fe41d74a4a71027def6ee85cc0579215",
"sealer.1":"697e81e512cffc55fc9c506104fb888a9ecf4e29eabfef6bb334b0ebb6fc4ef8fab60eb614a0f2be178d0b5993464c7387e2b284235402887cdf640f15cb2b4a",
"sealer.2":"8718579e9a6fee647b3d7404d59d66749862aeddef22e6b5abaafe1af6fc128fc33ed5a9a105abddab51e12004c6bfe9083727a1c3a22b067ddbaac3fa349f7f",
"sealer.3":"8fc9661baa057034f10efacfd8be3b7984e2f2e902f83c5c4e0e8a60804341426ace51492ffae087d96c0b968bd5e92fa53ea094ace8d1ba72de6e4515249011",
"nodeNum":4,
"omitEmptyBlock":true,
"protocolId":264,
"toView":2834
},
{
"prepareCache_blockHash":"0000000000000000000000000000000000000000000000000000000000000000",
"prepareCache_height":-1,
"prepareCache_idx":"115792089237316195423570985008687907853269984665640564039457584007913129639935",
"prepareCache_view":"115792089237316195423570985008687907853269984665640564039457584007913129639935"
},
{
"rawPrepareCache_blockHash":"0000000000000000000000000000000000000000000000000000000000000000",
"rawPrepareCache_height":-1,
"rawPrepareCache_idx":"115792089237316195423570985008687907853269984665640564039457584007913129639935",
"rawPrepareCache_view":"115792089237316195423570985008687907853269984665640564039457584007913129639935"
},
{
"committedPrepareCache_blockHash":"3ef0d7dd2bd4ef5b86290da3964043d42ed3282a96584fc773923f45e9b13624",
"committedPrepareCache_height":90,
"committedPrepareCache_idx":"0",
"committedPrepareCache_view":"15"
},
{
"futureCache_blockHash":"0000000000000000000000000000000000000000000000000000000000000000",
"futureCache_height":-1,
"futureCache_idx":"115792089237316195423570985008687907853269984665640564039457584007913129639935",
"futureCache_view":"115792089237316195423570985008687907853269984665640564039457584007913129639935"
},
{
"signCache_cachedSize":"0"
},
{
"commitCache_cachedSize":"0"
},
{
"viewChangeCache_cachedSize":"0"
}
]
getSyncStatus¶
运行getSyncStatus或gss,查看同步状态。
> gss
{
"blockNumber":90,
"genesisHash":"ca0577fa37367e89628e2db0a7adfccdd4b53e5fe781d7e34a3d5edbe29d1961",
"isSyncing":false,
"knownHighestNumber":0,
"knownLatestHash":"ca0577fa37367e89628e2db0a7adfccdd4b53e5fe781d7e34a3d5edbe29d1961",
"latestHash":"77e5b6d799edabaeae654ac5cea9baacd6f8e7ace33531d40c7ed65192de1f02",
"nodeId":"3106a6310b5edc07658d09cf6a96ba597a5cfc8fbec8587c6786112808286c11f2bc81db9133328983bc641f1c97ce38fe41d74a4a71027def6ee85cc0579215",
"peers":[
{
"blockNumber":90,
"genesisHash":"ca0577fa37367e89628e2db0a7adfccdd4b53e5fe781d7e34a3d5edbe29d1961",
"latestHash":"77e5b6d799edabaeae654ac5cea9baacd6f8e7ace33531d40c7ed65192de1f02",
"nodeId":"697e81e512cffc55fc9c506104fb888a9ecf4e29eabfef6bb334b0ebb6fc4ef8fab60eb614a0f2be178d0b5993464c7387e2b284235402887cdf640f15cb2b4a"
},
{
"blockNumber":90,
"genesisHash":"ca0577fa37367e89628e2db0a7adfccdd4b53e5fe781d7e34a3d5edbe29d1961",
"latestHash":"77e5b6d799edabaeae654ac5cea9baacd6f8e7ace33531d40c7ed65192de1f02",
"nodeId":"8718579e9a6fee647b3d7404d59d66749862aeddef22e6b5abaafe1af6fc128fc33ed5a9a105abddab51e12004c6bfe9083727a1c3a22b067ddbaac3fa349f7f"
},
{
"blockNumber":90,
"genesisHash":"ca0577fa37367e89628e2db0a7adfccdd4b53e5fe781d7e34a3d5edbe29d1961",
"latestHash":"77e5b6d799edabaeae654ac5cea9baacd6f8e7ace33531d40c7ed65192de1f02",
"nodeId":"8fc9661baa057034f10efacfd8be3b7984e2f2e902f83c5c4e0e8a60804341426ace51492ffae087d96c0b968bd5e92fa53ea094ace8d1ba72de6e4515249011"
}
],
"protocolId":265,
"txPoolSize":0
}
getClientVersion¶
运行getClientVersion或gcv,查看节点的版本。
> gcv
{
"Build Time":"20190107 10:15:23",
"Build Type":"Linux/g++/RelWithDebInfo",
"FISCO-BCOS Version":"2.0.0",
"Git Branch":"release-2.0.1",
"Git Commit Hash":"be95a6e3e85b621860b101c3baeee8be68f5f450"
}
getPeers¶
运行getPeers或gps,查看节点的peers。
> gps
[
{
"IPAndPort":"127.0.0.1:50723",
"NodeID":"8718579e9a6fee647b3d7404d59d66749862aeddef22e6b5abaafe1af6fc128fc33ed5a9a105abddab51e12004c6bfe9083727a1c3a22b067ddbaac3fa349f7f",
"Topic":[
]
},
{
"IPAndPort":"127.0.0.1:50719",
"NodeID":"697e81e512cffc55fc9c506104fb888a9ecf4e29eabfef6bb334b0ebb6fc4ef8fab60eb614a0f2be178d0b5993464c7387e2b284235402887cdf640f15cb2b4a",
"Topic":[
]
},
{
"IPAndPort":"127.0.0.1:30304",
"NodeID":"8fc9661baa057034f10efacfd8be3b7984e2f2e902f83c5c4e0e8a60804341426ace51492ffae087d96c0b968bd5e92fa53ea094ace8d1ba72de6e4515249011",
"Topic":[
]
}
]
getGroupPeers¶
运行getGroupPeers或ggp,查看节点所在group的peers。
> ggp
[3106a6310b5edc07658d09cf6a96ba597a5cfc8fbec8587c6786112808286c11f2bc81db9133328983bc641f1c97ce38fe41d74a4a71027def6ee85cc0579215, 8fc9661baa057034f10efacfd8be3b7984e2f2e902f83c5c4e0e8a60804341426ace51492ffae087d96c0b968bd5e92fa53ea094ace8d1ba72de6e4515249011, 8718579e9a6fee647b3d7404d59d66749862aeddef22e6b5abaafe1af6fc128fc33ed5a9a105abddab51e12004c6bfe9083727a1c3a22b067ddbaac3fa349f7f, 697e81e512cffc55fc9c506104fb888a9ecf4e29eabfef6bb334b0ebb6fc4ef8fab60eb614a0f2be178d0b5993464c7387e2b284235402887cdf640f15cb2b4a]
getBlockByHash¶
运行getBlockByHash或gbbh,根据区块哈希查询区块信息。参数:
- 区块哈希:0x开头的区块哈希值。
- 交易标志:默认false,区块中的交易只显示交易哈希,设置为true,显示交易具体信息。
> gbbh 0x77e5b6d799edabaeae654ac5cea9baacd6f8e7ace33531d40c7ed65192de1f02
{
"extraData":[
],
"gasLimit":"0x0",
"gasUsed":"0x0",
"hash":"0x77e5b6d799edabaeae654ac5cea9baacd6f8e7ace33531d40c7ed65192de1f02",
"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"number":"0x5a",
"parentHash":"0xf828e5ab29802b4cdd5275afed4b6dbc4b54e1a5b549fdc41530d2e6b5aab649",
"sealer":"0x0",
"stateRoot":"0x43be8be5a9edb857e7df502fab33775490ead2c37be6714a73c7893783a7845f",
"timestamp":"0x16764e744d0",
"transactions":[
"0xed82e2cda98db8614677aba1fa8a795820bd7f68a5919a2f85018ba8c10952ac"
],
"transactionsRoot":"0xe9d94814e825682d378fada5b680cc5359baca971de71804d683d3b87d1fd326"
}
> gbbh 0x77e5b6d799edabaeae654ac5cea9baacd6f8e7ace33531d40c7ed65192de1f02 true
{
"extraData":[
],
"gasLimit":"0x0",
"gasUsed":"0x0",
"hash":"0x77e5b6d799edabaeae654ac5cea9baacd6f8e7ace33531d40c7ed65192de1f02",
"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"number":"0x5a",
"parentHash":"0xf828e5ab29802b4cdd5275afed4b6dbc4b54e1a5b549fdc41530d2e6b5aab649",
"sealer":"0x0",
"stateRoot":"0x43be8be5a9edb857e7df502fab33775490ead2c37be6714a73c7893783a7845f",
"timestamp":"0x16764e744d0",
"transactions":[
{
"blockHash":"0x77e5b6d799edabaeae654ac5cea9baacd6f8e7ace33531d40c7ed65192de1f02",
"blockNumber":"0x5a",
"from":"0x7a5b31b49c6e944e9e1768785b1bc9a96cea0c17",
"gas":"0x1c9c380",
"gasPrice":"0x1",
"hash":"0xed82e2cda98db8614677aba1fa8a795820bd7f68a5919a2f85018ba8c10952ac",
"input":"0x10009562616c6963650000000000000000000000000000000000000000000000000000006a6f726500000000000000000000000000000000000000000000000000000000",
"nonce":"0x18711fff2ea68dc8b8dce4a3d3845c62a0630766a448bd9453a9127efe6f9e5",
"to":"0x738eedd873bb9722173194ab990c5b9a6c0beb25",
"transactionIndex":"0x0",
"value":"0x0"
}
],
"transactionsRoot":"0xe9d94814e825682d378fada5b680cc5359baca971de71804d683d3b87d1fd326"
}
getBlockByNumber¶
运行getBlockByNumber或gbbn,根据区块高度查询区块信息。参数:
- 区块高度:十进制整数。
- 交易标志:默认false,区块中的交易只显示交易哈希,设置为true,显示交易具体信息。
> gbbn 90
{
"extraData":[
],
"gasLimit":"0x0",
"gasUsed":"0x0",
"hash":"0x77e5b6d799edabaeae654ac5cea9baacd6f8e7ace33531d40c7ed65192de1f02",
"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"number":"0x5a",
"parentHash":"0xf828e5ab29802b4cdd5275afed4b6dbc4b54e1a5b549fdc41530d2e6b5aab649",
"sealer":"0x0",
"stateRoot":"0x43be8be5a9edb857e7df502fab33775490ead2c37be6714a73c7893783a7845f",
"timestamp":"0x16764e744d0",
"transactions":[
"0xed82e2cda98db8614677aba1fa8a795820bd7f68a5919a2f85018ba8c10952ac"
],
"transactionsRoot":"0xe9d94814e825682d378fada5b680cc5359baca971de71804d683d3b87d1fd326"
}
getBlockHashByNumber¶
运行getBlockHashByNumber或ghbn,通过区块高度获得区块哈希。参数:
- 区块高度:十进制整数。
> ghbn 90
0x77e5b6d799edabaeae654ac5cea9baacd6f8e7ace33531d40c7ed65192de1f02
getTransactionByHash¶
运行getTransactionByHash或gtbh,通过交易哈希查询交易信息。参数:
- 交易哈希:0x开头的交易哈希值。
> gtbh 0xed82e2cda98db8614677aba1fa8a795820bd7f68a5919a2f85018ba8c10952ac
{
"blockHash":"0x77e5b6d799edabaeae654ac5cea9baacd6f8e7ace33531d40c7ed65192de1f02",
"blockNumber":"0x5a",
"from":"0x7a5b31b49c6e944e9e1768785b1bc9a96cea0c17",
"gas":"0x1c9c380",
"gasPrice":"0x1",
"hash":"0xed82e2cda98db8614677aba1fa8a795820bd7f68a5919a2f85018ba8c10952ac",
"input":"0x10009562616c6963650000000000000000000000000000000000000000000000000000006a6f726500000000000000000000000000000000000000000000000000000000",
"nonce":"0x18711fff2ea68dc8b8dce4a3d3845c62a0630766a448bd9453a9127efe6f9e5",
"to":"0x738eedd873bb9722173194ab990c5b9a6c0beb25",
"transactionIndex":"0x0",
"value":"0x0"
}
getTransactionByBlockHashAndIndex¶
运行getTransactionByBlockHashAndIndex或gthi,通过区块哈希和交易索引查询交易信息。参数:
- 区块哈希:0x开头的区块哈希值。
- 交易索引:十进制整数。
> gthi 0x77e5b6d799edabaeae654ac5cea9baacd6f8e7ace33531d40c7ed65192de1f02 0
{
"blockHash":"0x77e5b6d799edabaeae654ac5cea9baacd6f8e7ace33531d40c7ed65192de1f02",
"blockNumber":"0x5a",
"from":"0x7a5b31b49c6e944e9e1768785b1bc9a96cea0c17",
"gas":"0x1c9c380",
"gasPrice":"0x1",
"hash":"0xed82e2cda98db8614677aba1fa8a795820bd7f68a5919a2f85018ba8c10952ac",
"input":"0x10009562616c6963650000000000000000000000000000000000000000000000000000006a6f726500000000000000000000000000000000000000000000000000000000",
"nonce":"0x18711fff2ea68dc8b8dce4a3d3845c62a0630766a448bd9453a9127efe6f9e5",
"to":"0x738eedd873bb9722173194ab990c5b9a6c0beb25",
"transactionIndex":"0x0",
"value":"0x0"
}
getTransactionByBlockNumberAndIndex¶
运行getTransactionByBlockNumberAndIndex或gtni,通过区块高度和交易索引查询交易信息。参数:
- 区块高度:十进制整数。
- 交易索引:十进制整数。
> gtni 90 0
{
"blockHash":"0x77e5b6d799edabaeae654ac5cea9baacd6f8e7ace33531d40c7ed65192de1f02",
"blockNumber":"0x5a",
"from":"0x7a5b31b49c6e944e9e1768785b1bc9a96cea0c17",
"gas":"0x1c9c380",
"gasPrice":"0x1",
"hash":"0xed82e2cda98db8614677aba1fa8a795820bd7f68a5919a2f85018ba8c10952ac",
"input":"0x10009562616c6963650000000000000000000000000000000000000000000000000000006a6f726500000000000000000000000000000000000000000000000000000000",
"nonce":"0x18711fff2ea68dc8b8dce4a3d3845c62a0630766a448bd9453a9127efe6f9e5",
"to":"0x738eedd873bb9722173194ab990c5b9a6c0beb25",
"transactionIndex":"0x0",
"value":"0x0"
}
getTransactionReceipt¶
运行getTransactionReceipt或gtr,通过交易哈希查询交易回执。参数:
- 交易哈希:0x开头的交易哈希值。
> gtr 0xed82e2cda98db8614677aba1fa8a795820bd7f68a5919a2f85018ba8c10952ac
{
"blockHash":"0x77e5b6d799edabaeae654ac5cea9baacd6f8e7ace33531d40c7ed65192de1f02",
"blockNumber":"0x5a",
"contractAddress":"0x0000000000000000000000000000000000000000",
"from":"0x7a5b31b49c6e944e9e1768785b1bc9a96cea0c17",
"gasUsed":"0xf401",
"logs":[
],
"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"status":"0x0",
"to":"0x738eedd873bb9722173194ab990c5b9a6c0beb25",
"transactionHash":"0xed82e2cda98db8614677aba1fa8a795820bd7f68a5919a2f85018ba8c10952ac",
"transactionIndex":"0x0"
}
getCode¶
运行getCode或gc,根据合约地址查询合约代码。参数:
- 合约地址:0x的合约地址(部署合约可以获得合约地址)。
> gc 0x97b8c19598fd781aaeb53a1957ef9c8acc59f705
0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806366c99139146100465780636d4ce63c14610066575bfe5b341561004e57fe5b610064600480803590602001909190505061008c565b005b341561006e57fe5b61007661028c565b6040518082815260200191505060405180910390f35b8060006001015410806100aa57506002600101548160026001015401105b156100b457610289565b806000600101540360006001018190555080600260010160008282540192505081905550600480548060010182816100ec919061029a565b916000526020600020906004020160005b608060405190810160405280604060405190810160405280600881526020017f32303137303431330000000000000000000000000000000000000000000000008152508152602001600060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600260000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200185815250909190915060008201518160000190805190602001906101ec9291906102cc565b5060208201518160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060408201518160020160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550606082015181600301555050505b50565b600060026001015490505b90565b8154818355818115116102c7576004028160040283600052602060002091820191016102c6919061034c565b5b505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061030d57805160ff191683800117855561033b565b8280016001018555821561033b579182015b8281111561033a57825182559160200191906001019061031f565b5b50905061034891906103d2565b5090565b6103cf91905b808211156103cb57600060008201600061036c91906103f7565b6001820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556002820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600382016000905550600401610352565b5090565b90565b6103f491905b808211156103f05760008160009055506001016103d8565b5090565b90565b50805460018160011615610100020316600290046000825580601f1061041d575061043c565b601f01602090049060005260206000209081019061043b91906103d2565b5b505600a165627a7a723058203c1f95b4a803493db0120df571d9f5155734152548a532412f2f9fa2dbe1ac730029
getTotalTransactionCount¶
运行getTotalTransactionCount或gtc,查询当前块高和总交易数。
> gtc
{
"blockNumber":1,
"txSum":1
}
deploy¶
运行deploy或d,部署合约。(默认提供HelloWorld合约进行示例使用) 参数:
- 合约名称:部署的合约名称。
> deploy HelloWorld
0xb3c223fc0bf6646959f254ac4e4a7e355b50a344
注: 部署用户编写的合约,只需要将solidity合约文件放到solidity/contracts/目录下,然后进行部署即可。
call¶
运行call或c,调用合约。参数:
- 合约名称:部署的合约名称。
- 合约地址: 部署合约获取的地址。
- 合约接口名:调用的合约接口名。
- 参数:由合约接口参数决定,参数空格分隔,字符串需要双引号标识。
# 调用get接口获取name变量
> call HelloWorld 0xb3c223fc0bf6646959f254ac4e4a7e355b50a344 get
Hello, World!
# 调用set设置name
> call HelloWorld 0xb3c223fc0bf6646959f254ac4e4a7e355b50a344 set "Hello,FISCO-BCOS"
0x21dca087cb3e44f44f9b882071ec6ecfcb500361cad36a52d39900ea359d0895
# 调用get接口获取name变量,检查设置是否生效
> call HelloWorld 0xb3c223fc0bf6646959f254ac4e4a7e355b50a344 get
Hello,FISCO-BCOS
deployByCNS¶
deployByCNS或dbc,利用CNS部署合约。(默认提供HelloWorld合约进行示例使用)参数:
- 合约名称:部署的合约名称。
- 合约版本号:部署的合约版本号(长度不能超过40)。
> dbc HelloWorld 1.0
0x7956881392a8e2893d6c3f514ef5c37f9d5e52ef
注: 部署用户编写的合约,只需要将solidity合约文件放到solidity/contracts/目录下,然后进行部署即可。
queryCNS¶
运行queryCNS或qcs,根据合约名称和合约版本号(可选参数)查询CNS表记录信息。参数:
- 合约名称:部署的合约名称。
- 合约版本号:(可选)部署的合约版本号。
> qcs HelloWorld
---------------------------------------------------------------------------------------------
| version | address |
| 1.0 | 0x7956881392a8e2893d6c3f514ef5c37f9d5e52ef |
| 2.0 | 0x18f18eb950ae04b3b45837261e441faf2d316341 |
---------------------------------------------------------------------------------------------
> qcs HelloWorld 1.0
---------------------------------------------------------------------------------------------
| version | address |
| 1.0 | 0x7956881392a8e2893d6c3f514ef5c37f9d5e52ef |
---------------------------------------------------------------------------------------------
callByCNS¶
运行callByCNS或cbc,利用CNS调用合约。参数:
- 合约名称:部署的合约名称。
- 合约版本号:部署的合约版本号。
- 合约接口名:调用的合约接口名。
- 参数:由合约接口参数决定,参数空格分隔,字符串需要双引号标识。
> cbc HelloWorld 1.0 set "Hello,FISCO-BCOS"
0x80bb37cc8de2e25f6a1cdcb6b4a01ab5b5628082f8da4c48ef1bbc1fb1d28b2d
> cbc HelloWorld 1.0 get
Hello,FISCO-BCOS
addSealer¶
运行addSealer或as,将节点添加为共识节点。参数:
- 节点nodeID
> as ea2ca519148cafc3e92c8d9a8572b41ea2f62d0d19e99273ee18cccd34ab50079b4ec82fe5f4ae51bd95dd788811c97153ece8c05eac7a5ae34c96454c4d3123
{
"code":1,
"msg":"success"
}
addObserver¶
运行addObserver或ao,将节点添加为观察节点。参数:
- 节点nodeID
> ao ea2ca519148cafc3e92c8d9a8572b41ea2f62d0d19e99273ee18cccd34ab50079b4ec82fe5f4ae51bd95dd788811c97153ece8c05eac7a5ae34c96454c4d3123
{
"code":1,
"msg":"success"
}
removeNode¶
运行removeNode或rn,节点退出。通过am/ao命令可以将退出的节点添加为共识/观察节点。参数:
- 节点nodeID
> rn ea2ca519148cafc3e92c8d9a8572b41ea2f62d0d19e99273ee18cccd34ab50079b4ec82fe5f4ae51bd95dd788811c97153ece8c05eac7a5ae34c96454c4d3123
{
"code":1,
"msg":"success"
}
setSystemConfigByKey¶
运行setSystemConfigByKey或ssc,以键值对方式设置系统配置。目前设置的系统配置支持tx_count_limit和tx_gas_limit,系统配置用法见多群组操作指南参数:
- key
- value
> ssc tx_count_limit 100
{
"code":1,
"msg":"success"
}
addUserTableManager¶
运行addUserTableManager或aum,根据用户表名和外部账号地址设置权限信息。参数:
- 表名
- 外部账号地址
> aum t_test 0xc0d0e6ccc0b44c12196266548bec4a3616160e7d
{
"code":1,
"msg":"success"
}
removeUserTableManager¶
运行removeUserTableManager或rum,根据用户表名和外部账号地址移除权限信息。参数:
- 表名
- 外部账号地址
> rum t_test 0xc0d0e6ccc0b44c12196266548bec4a3616160e7d
{
"code":1,
"msg":"success"
}
queryUserTableManager¶
运行queryUserTableManager或qum,根据用户表名查询设置的权限记录列表。参数:
- 表名
> qum t_test
---------------------------------------------------------------------------------------------
| address | enable_num |
| 0xc0d0e6ccc0b44c12196266548bec4a3616160e7d | 2 |
---------------------------------------------------------------------------------------------
addDeployAndCreateManager¶
运行addDeployAndCreateManager或adm,增加外部账号地址的部署合约和创建用户表权限。
参数:
- 外部账号地址
> adm 0xc0d0e6ccc0b44c12196266548bec4a3616160e7d
{
"code":1,
"msg":"success"
}
removeDeployAndCreateManager¶
运行removeDeployAndCreateManager或rdm,移除外部账号地址的部署合约和创建用户表权限。参数:
- 外部账号地址
> rdm 0xc0d0e6ccc0b44c12196266548bec4a3616160e7d
{
"code":1,
"msg":"success"
}
queryDeployAndCreateManager¶
运行queryDeployAndCreateManager或qdm,查询拥有部署合约和创建用户表权限的权限记录列表。
> qdm
---------------------------------------------------------------------------------------------
| address | enable_num |
| 0xc0d0e6ccc0b44c12196266548bec4a3616160e7d | 2 |
---------------------------------------------------------------------------------------------
addNodeManager¶
运行addNodeManager或anm,增加外部账号地址的节点管理权限。
参数:
- 外部账号地址
> anm 0xc0d0e6ccc0b44c12196266548bec4a3616160e7d
{
"code":1,
"msg":"success"
}
removeNodeManager¶
运行removeNodeManager或rnm,移除外部账号地址的节点管理权限。参数:
- 外部账号地址
> rnm 0xc0d0e6ccc0b44c12196266548bec4a3616160e7d
{
"code":1,
"msg":"success"
}
queryNodeManager¶
运行queryNodeManager或qnm,查询拥有节点管理的权限记录列表。
> qnm
---------------------------------------------------------------------------------------------
| address | enable_num |
| 0xc0d0e6ccc0b44c12196266548bec4a3616160e7d | 2 |
---------------------------------------------------------------------------------------------
addCNSManager¶
运行addCNSManager或acm,增加外部账号地址的使用CNS权限。
参数:
- 外部账号地址
> acm 0xc0d0e6ccc0b44c12196266548bec4a3616160e7d
{
"code":1,
"msg":"success"
}
removeCNSManager¶
运行removeCNSManager或rcm,移除外部账号地址的使用CNS权限。参数:
- 外部账号地址
> rcm 0xc0d0e6ccc0b44c12196266548bec4a3616160e7d
{
"code":1,
"msg":"success"
}
queryCNSManager¶
运行queryCNSManager或qcm,查询拥有使用CNS的权限记录列表。
> qcm
---------------------------------------------------------------------------------------------
| address | enable_num |
| 0xc0d0e6ccc0b44c12196266548bec4a3616160e7d | 2 |
---------------------------------------------------------------------------------------------
addSysConfigManager¶
运行addSysConfigManager或asm,增加外部账号地址的系统参数管理权限。
参数:
- 外部账号地址
> asm 0xc0d0e6ccc0b44c12196266548bec4a3616160e7d
{
"code":1,
"msg":"success"
}
removeSysConfigManager¶
运行removeSysConfigManager或rsm,移除外部账号地址的系统参数管理权限。参数:
- 外部账号地址
> rsm 0xc0d0e6ccc0b44c12196266548bec4a3616160e7d
{
"code":1,
"msg":"success"
}
querySysConfigManager¶
运行querySysConfigManager或qsm,查询拥有系统参数管理的权限记录列表。
> qsm
---------------------------------------------------------------------------------------------
| address | enable_num |
| 0xc0d0e6ccc0b44c12196266548bec4a3616160e7d | 2 |
---------------------------------------------------------------------------------------------
节点准入管理手册¶
本文档描述节点准入管理的实践操作,建议阅读本操作文档前请先行了解《节点准入管理介绍》。
操作方式¶
- 修改节点配置:节点修改自身配置后重启生效,涉及的操作项目包括网络的加入/退出、CA黑名单的列入/移除。
- 交易共识上链:节点发送上链交易修改需群组共识的配置项,涉及的操作项目包括节点类型的修改。目前提供的发送交易途径为控制台、SDK提供的precompiled service接口。
- RPC查询:使用curl命令查询链上信息,涉及的操作项目包括群组节点的查询。
操作示例¶
本节将以下图为例对上述六种操作进行描述。虚线表示节点间能进行网络通信,实线表示节点间在可通信的基础上具备群组关系,不同颜色区分不同的群组关系。图中有一个网络,包含三个群组,其中群组Group3有三个节点。Group3是否与其他群组存在交集节点,不影响以下操作过程的通用性。
Group3的相关节点信息举例为:
节点1的目录名为node0,IP端口为127.0.0.1:30400,nodeID前四个字节为b231b309…
节点2的目录名为node1,IP端口为127.0.0.1:30401,nodeID前四个字节为aab37e73…
节点3的目录名为node2,IP端口为127.0.0.1:30402,nodeID前四个字节为d6b01a96…
A节点加入网络¶
场景描述:
节点3原先不在网络中,现在加入网络。
操作顺序:
1 . 执行tools/gen_node.sh
生成节点目录,目录名以node2为例,node2内有conf/
目录;
# 在目录tools下执行
$ ./gen_node.sh -c nodes/cert/agency -o node2
2 . 拷贝node2到nodes/127.0.0.1/
下,与其他节点目录(node0、node1)同级;
3 . 拷贝node0/config.ini
到node2目录;
4 . 修改node2/config.ini
。对于[rpc]
模块,修改listen_ip
、channel_listen_port
和jsonrpc_listen_port
;对于[p2p]
模块,修改listen_port
并在node.
中增加自身节点信息;
$ vim node2/config.ini
[rpc]
;rpc listen ip
listen_ip=127.0.0.1
;channelserver listen port
channel_listen_port=20302
;jsonrpc listen port
jsonrpc_listen_port=8647
[p2p]
;p2p listen ip
listen_ip=0.0.0.0
;p2p listen port
listen_port=30402
;nodes to connect
node.0=127.0.0.1:30400
node.1=127.0.0.1:30401
node.2=127.0.0.1:30402
5 . 执行node2/start.sh
启动节点3;
6 . 确认节点3与节点1和节点2的连接已经建立,加入网络操作完成。
# 在打开DEBUG级别日志前提下,查看自身节点(node2)连接的节点数及所连接的节点信息(nodeID)
# 以下日志表明节点node2与两个节点(节点的nodeID前4个字节为b231b309、aab37e73)建立了连接
$ tail -f node2/log/log* | grep P2P
debug|2019-02-21 10:30:18.694258| [P2P][Service] heartBeat ignore connected,endpoint=127.0.0.1:30400,nodeID=b231b309...
debug|2019-02-21 10:30:18.694277| [P2P][Service] heartBeat ignore connected,endpoint=127.0.0.1:30401,nodeID=aab37e73...
info|2019-02-21 10:30:18.694294| [P2P][Service] heartBeat connected count,size=2
补充说明:
- 从节点1拷贝过来的
config.ini
的其余配置可保持不变; - 节点1和2不需修改自身的P2P节点连接列表;
- 步骤1中所选择的群组建议为节点3后续需加入的群组。
A节点退出网络¶
场景描述:
节点3已在网络中,与节点1和节点2通信,现在退出网络。
操作顺序:
1 . 对于节点3,将自身的P2P节点连接列表内容清空,重启节点3;
# 在node2目录下执行
$ ./stop.sh
$ ./start.sh
nohup: appending output to ‘nohup.out’
2 . 对于节点1和2,将节点3从自身的P2P节点连接列表中移除(如有),重启节点1和2;
3 . 确认节点3与节点1(和2)的原有连接已经断开,退出网络操作完成。
补充说明:
- 节点3需先退出群组再退出网络,退出顺序由用户保证,系统不再作校验;
- 网络连接由节点主动发起,如缺少第2步,节点3仍可感知节点1和节点2发起的P2P连接请求,并建立连接;
- 如果节点3想拒绝节点1和节点2的连接请求,可参考A节点将B节点列入CA黑名单进行操作。
A节点加入群组¶
场景描述:
群组Group3原有节点1和节点2,两节点轮流出块,现在将节点3加入群组。
操作顺序:
- 节点3加入网络;
- 节点3拷贝节点1(或2)的
group.3.genesis
(内含群组节点初始列表)和group.3.ini
到对应位置,不需改动; - 重启节点3;
- 参考操作工具一节,根据节点3的nodeID设置节点3为共识节点;
- 参考操作工具一节,查询group3的共识节点中是否包含节点3的nodeID,如存在,加入群组操作完成。
补充说明:
- 节点3首次启动会将配置的群组节点初始列表内容写入群组节点系统表,区块同步结束后,群组各节点的群组节点系统表均一致;
- 节点3需先完成网络准入后,再执行加入群组的操作,系统将校验操作顺序;
- 节点3的群组固定配置文件需与节点1和2的一致。
A节点退出群组¶
场景描述:
群组Group3原有节点1、节点2和节点3,三节点轮流出块,现在将节点3退出群组。
操作顺序:
- 参考操作工具一节,根据节点3的NodeID设置节点3为游离节点;
- 参考操作工具一节,查询group3的共识节点中是否包含节点3的nodeID,如已消失,退出群组操作完成。
补充说明:
- 节点3可以共识节点或观察节点的身份执行退出操作。
A节点将B节点列入CA黑名单¶
场景描述:
节点1和节点2在群组Group3中,三节点轮流出块,现在节点1将节点2加入自身黑名单。
操作顺序:
1 . 对于节点1(node0),将节点2(node1)的公钥nodeID加入自身的CA黑名单;
$ cat node1/conf/node.nodeid
aab37e73489bbd277aa848a99229ab70b6d6d4e1b81a715a22608a62f0f5d4270d7dd887394e78bd02d9f31b8d366ce4903481f50b1f44f0e4fda67149208943
$ vim node0/config.ini
;certificate rejected list
[crl]
;crl.0 should be nodeid, nodeid's length is 128
crl.0=aab37e73489bbd277aa848a99229ab70b6d6d4e1b81a715a22608a62f0f5d4270d7dd887394e78bd02d9f31b8d366ce4903481f50b1f44f0e4fda67149208943
2 . 重启节点1;
3 . 确认节点1与节点2的原有连接已经断开,加入黑名单操作完成。
补充说明:
- 节点1添加节点2到自身CA黑名单的操作,将断开与节点2的网络连接及AMOP通信;
- 节点1添加节点2到自身CA黑名单的操作,将对节点1所在群组Group3的共识及同步消息/数据转发。
A节点将B节点移除CA黑名单¶
场景描述:
节点1的CA黑名单中有节点2的nodeID,节点2的CA黑名单中没有节点1的nodeID,现在节点1将节点2移除自身的CA黑名单。
操作顺序:
- 对于节点1,将节点2的公钥nodeID从自身的CA黑名单移除;
- 重启节点1;
- 确认节点1与节点2重新建立连接,移除黑名单操作完成。
操作工具¶
控制台¶
控制台提供的命令包括:
- addSealer:根据节点NodeID设置对应节点为共识节点
- addObserver:根据节点NodeID设置对应节点为观察节点
- removeNode:根据节点NodeID设置对应节点为游离节点
- getSealerList:查看群组中共识节点列表
- getObserverList:查看群组中观察节点列表
控制台详细使用方法请参考《控制台》。
CA黑名单操作手册¶
本文档描述CA黑名单的实践操作,建议阅读本操作文档前请先行了解《CA黑名单介绍》。
CA黑名单的操作包括一节点将他节点列入/移除CA黑名单,通过修改配置文件重启实现。
修改范围¶
节点config.ini
配置有[crl]
路径(可选)。[crl]
为节点NodeID列表,node.X为本节点拒绝连接的对方节点NodeID。
修改示例¶
网络中存在三个节点,均互联,节点相关信息为:
节点1的目录名为node0,IP端口为127.0.0.1:30400,nodeID前四个字节为b231b309…
节点2的目录名为node1,IP端口为127.0.0.1:30401,nodeID前四个字节为aab37e73…
节点3的目录名为node2,IP端口为127.0.0.1:30402,nodeID前四个字节为d6b01a96…
A节点将B节点列入CA黑名单¶
场景描述:
节点1和节点2在群组同一群组中,与群组其余节点轮流出块,现在节点1将节点2加入自身黑名单。
操作顺序:
1 . 对于节点1(node0),将节点2(node1)的公钥nodeID加入自身的CA黑名单;
$ cat node1/conf/node.nodeid
aab37e73489bbd277aa848a99229ab70b6d6d4e1b81a715a22608a62f0f5d4270d7dd887394e78bd02d9f31b8d366ce4903481f50b1f44f0e4fda67149208943
$ vim node0/config.ini
;certificate rejected list
[crl]
;crl.0 should be nodeid, nodeid's length is 128
crl.0=aab37e73489bbd277aa848a99229ab70b6d6d4e1b81a715a22608a62f0f5d4270d7dd887394e78bd02d9f31b8d366ce4903481f50b1f44f0e4fda67149208943
2 . 重启节点1;
# 在node1目录下执行
$ ./stop.sh
$ ./start.sh
nohup: appending output to ‘nohup.out’
3 . 通过日志确认节点1与节点2的原有连接已经断开,加入黑名单操作完成。
# 在打开DEBUG级别日志前提下,查看自身节点(node2)连接的节点数及所连接的节点信息(nodeID)
# 以下日志表明节点node2与两个节点(节点的nodeID前4个字节为b231b309、aab37e73)建立了连接
$ tail -f node2/log/log* | grep P2P
debug|2019-02-21 10:30:18.694258| [P2P][Service] heartBeat ignore connected,endpoint=127.0.0.1:30400,nodeID=b231b309...
debug|2019-02-21 10:30:18.694277| [P2P][Service] heartBeat ignore connected,endpoint=127.0.0.1:30401,nodeID=aab37e73...
info|2019-02-21 10:30:18.694294| [P2P][Service] heartBeat connected count,size=2
补充说明:
- 节点1添加节点2到自身CA黑名单的操作,将断开与节点2的网络连接及AMOP通信;
- 节点1添加节点2到自身CA黑名单的操作,将对节点1所在群组的共识及同步消息/数据转发。
A节点将B节点移除CA黑名单¶
场景描述:
节点1的CA黑名单中有节点2的nodeID,节点2的CA黑名单中没有节点1的nodeID,现在节点1将节点2移除自身的CA黑名单。
操作顺序:
- 对于节点1,将节点2的公钥NodeID从自身的CA黑名单移除;
- 重启节点1;
- 通过日志确认确认节点1与节点2重新建立连接,移除黑名单操作完成。
FAQ¶
1、 节点A添加节点B为CA黑名单后,节点A所在的部分账本无法共识?
节点A添加节点B为CA黑名单并重启后,节点A和节点B将不建立连接,无法进行P2P通信,继而无法发送/接收共识消息。当节点A和B的共同群组中有效节点数不满足3f+1容错条件时,群组将无法共识。
权限控制手册¶
本文档描述权限控制的实践操作,有关权限控制的详细设计请参考权限控制设计文档。
3 权限控制工具¶
3.1 控制台工具¶
控制台权限控制命令¶
针对普通用户,FISCO BCOS提供控制台命令使用权限功能(针对开发者,可以调用SDK API的AuthorityService接口使用权限功能),其中涉及的权限控命令如下:
命令全称 | 命令缩写 | 命令参数 | 功能 |
---|---|---|---|
addDeployAndCreateManager | adm | address | 增加外部账号地址的部署合约和创建用户表权限 |
removeDeployAndCreateManager | rdm | address | 移除外部账号地址的部署合约和创建用户表权限 |
queryDeployAndCreateManager | qdm | 查询拥有部署合约和创建用户表权限的权限记录列表 | |
addUserTableManager | aum | table_name address | 根据用户表名和外部账号地址设置权限信息 |
removeUserTableManager | rum | table_name address | 根据用户表名和外部账号地址移除权限信息 |
queryUserTableManager | qum | table_name | 根据用户表名查询设置的权限记录列表 |
addNodeManager | anm | address | 增加外部账号地址的节点管理权限 |
removeNodeManager | rnm | address | 移除外部账号地址的节点管理权限 |
queryNodeManager | qnm | 查询拥有节点管理的权限记录列表 | |
addCNSManager | acm | address | 增加外部账号地址的使用CNS权限 |
removeCNSManager | rcm | address | 移除外部账号地址的使用CNS权限 |
queryCNSManager | qcm | 查询拥有使用CNS的权限记录列表 | |
addSysConfigManager | asm | address | 增加外部账号地址的系统参数管理权限 |
removeSysConfigManager | rsm | address | 移除外部账号地址的系统参数管理权限 |
querySysConfigManager | qsm | 查询拥有系统参数管理的权限记录列表 | |
addAuthorityManager | aam | address | 增加外部账号地址的管理权限的权限 |
removeAuthorityManager | ram | address | 移除外部账号地址的管理权限的权限 |
queryAuthorityManager | qam | 查询拥有管理权限的权限记录列表 |
3.2 示例客户端工具¶
提供权限控制示例客户端工具,该示例工具可以指定三个外部账号进行部署合约、创建用户表t_test以及对用户表t_test进行增删改查操作。其中三个外部账号地址如下:
1: tx.origin = 0xf1585b8d0e08a0a00fff662e24d67ba95a438256
2: tx.origin = 0xc0d0e6ccc0b44c12196266548bec4a3616160e7d
3: tx.origin = 0x1600e34312edea101d8b41a3465f2e381b66baed
获取客户端工具:
curl -LO https://github.com/FISCO-BCOS/LargeFiles/raw/master/tools/bcosclient_authority2.0.tar.gz
解压客户端工具,具体使用参考解压目录中的使用说明文档。
4 权限控制示例¶
4.1 部署合约和创建用户表控制示例¶
4.1.1 默认权限示例¶
默认外部账号均可部署合约和创建表。
4.1.1.1 合约部署示例¶
登录控制台,查询拥有部署合约和创建用户表权限的权限记录。
> qdm
Empty set.
确认初始状态无部署合约和创建用户表权限的权限记录,默认所有外部账号均可以部署合约。
外部账号1部署合约: 进入客户端工具dist目录,运行部署合约命令:
$ ./run.sh 1 1 deploy 1 1 deploy tx.origin = 0xf1585b8d0e08a0a00fff662e24d67ba95a438256 deploy contract address: 0x9e116ecf100be281ae9587c907cf5b450d51af1b deploy contract successful!
外部账号1部署合约成功。
外部账号2部署合约:
$ ./run.sh 2 1 deploy 2 1 deploy tx.origin = 0xc0d0e6ccc0b44c12196266548bec4a3616160e7d deploy contract address: 0xa5b85cf3728a15b67572af9b180e2ab4e449359a deploy contract successful!
外部账号2部署合约成功。
外部账号3部署合约:
$ ./run.sh 3 1 deploy 3 1 deploy tx.origin = 0x1600e34312edea101d8b41a3465f2e381b66baed deploy contract address: 0xd4ebb24ac68263e92335977c7ea968d5e770eb07 deploy contract successful!
外部账号3部署合约成功。
4.1.1.2 创建用户表示例¶
外部账号1创建用户表t_test: 进入客户端工具dist目录,运行创建用户表命令:
$ ./run.sh 1 1 create 1 1 create create t_test table completed.
外部账号1创建t_test表成功,表明有权限创建用户表。类似的,外部账号2和3也可也创建用户表t_test。
4.1.2 设置权限示例¶
登录控制台,设置外部账号1拥有部署合约和创建用户表的权限。
> adm 0xf1585b8d0e08a0a00fff662e24d67ba95a438256
{
"code":1,
"msg":"success"
}
> qdm
---------------------------------------------------------------------------------------------
| address | enable_num |
| 0xf1585b8d0e08a0a00fff662e24d67ba95a438256 | 1 |
---------------------------------------------------------------------------------------------
4.1.2.1 合约部署示例¶
外部账号1部署合约:
$ ./run.sh 1 1 deploy 1 1 deploy tx.origin = 0xf1585b8d0e08a0a00fff662e24d67ba95a438256 deploy contract address: 0x31877d5864125b3aa3a5ae60022274d1a4130d00 deploy contract successful!
外部账号1部署合约成,有权限部署合约。
外部账号2部署合约:
$ ./run.sh 2 1 deploy 2 1 deploy tx.origin = 0xc0d0e6ccc0b44c12196266548bec4a3616160e7d non-authorized to deploy contracts!
外部账号2部署合约失败,无权限部署合约。
外部账号3部署合约:
$ ./run.sh 3 1 deploy 3 1 deploy tx.origin = 0x1600e34312edea101d8b41a3465f2e381b66baed non-authorized to deploy contracts!
外部账号3部署合约失败,无权限部署合约。
4.1.1.2 创建用户表示例¶
外部账号2创建用户表t_test:
$ ./run.sh 2 1 create 2 1 create non-authorized to create t_test table.
外部账号2创建t_test表失败,表明无权限创建用户表。
外部账号3创建用户表t_test:
$ ./run.sh 3 1 create 3 1 create non-authorized to create t_test table.
外部账号3创建t_test表失败,表明无权限创建用户表。
外部账号1创建用户表t_test:
$ ./run.sh 1 1 create 1 1 create create t_test table completed.
外部账号1创建t_test表成功,表明有权限创建用户表。
4.1.3 移除权限示例¶
登录控制台,移除设置的外部账号1权限,则外部账号1,2和3均可以部署合约和创建用户表。
> rdm 0xf1585b8d0e08a0a00fff662e24d67ba95a438256
{
"code":1,
"msg":"success"
}
> qdm
Empty set.
4.2 用户表控制示例¶
4.2.1 默认权限示例¶
通过示例客户端分别指定三个外部账号进行t_test表的增删改查示例。首先登录控制台,查询t_test表的权限设置记录。
> qum t_test
Empty set.
确认初始状态没有设置权限,因此默认所有外部账号均可以对t_test进行读写操作。
4.2.1.1 账号1示例¶
指定外部账号1创建t_test表:
$ ./run.sh 1 1 create
1 1 create
tx.origin = 0xf1585b8d0e08a0a00fff662e24d67ba95a438256
create t_test table completed.
t_test表创建成功。
通过指定外部账号1向t_test表插入记录:
$ ./run.sh 1 1 insert fruit 1 apple1 1 1 insert fruit 1 apple1 tx.origin = 0xf1585b8d0e08a0a00fff662e24d67ba95a438256 insertCount = 1
t_test表插入记录成功。
通过指定外部账号1向t_test表查询记录:
$ ./run.sh 1 1 select fruit 1 1 select fruit tx.origin = 0xf1585b8d0e08a0a00fff662e24d67ba95a438256 record numbers = 1 name = fruit item_id = 1 item_name = apple1
通过指定外部账号1向t_test表更新记录:
$ ./run.sh 1 1 update fruit 1 apple11 1 1 update fruit 1 apple11 tx.origin = 0xf1585b8d0e08a0a00fff662e24d67ba95a438256 updateCount = 1
t_test表更新记录成功,可以通过查询记录再次验证。
通过指定外部账号1向t_test表删除记录:
$ ./run.sh 1 1 remove fruit 1 1 1 remove fruit 1 tx.origin = 0xf1585b8d0e08a0a00fff662e24d67ba95a438256 removeCount = 1
t_test表删除记录成功,可以通过查询记录再次验证。
4.2.1.2 账号2和3示例¶
外部账号1已创建t_test表,无需再创建。与示例外部账号1类似,可以分别指定外部账号2和3对t_test表进行增删改查验证。
4.2.2 设置权限示例¶
登录控制台,设置外部账号1可以对t_test表进行读写操作。
> aum t_test 0xf1585b8d0e08a0a00fff662e24d67ba95a438256
{
"code":1,
"msg":"success"
}
> qum t_test
---------------------------------------------------------------------------------------------
| address | enable_num |
| 0xf1585b8d0e08a0a00fff662e24d67ba95a438256 | 11 |
---------------------------------------------------------------------------------------------
设置完毕后,则外部账号1有权限对t_test表进行读写操作,其他外部账号只可以对t_test表执行读操作。
外部账号1有权限操作t_test表示例:
通过指定外部账号1向t_test表插入记录:
$ ./run.sh 1 1 insert fruit 2 apple1 1 1 insert fruit 2 apple1 tx.origin = 0xf1585b8d0e08a0a00fff662e24d67ba95a438256 insertCount = 1
t_test表插入记录成功。
通过指定外部账号1向t_test表查询记录:
$ ./run.sh 1 1 select fruit 1 1 select fruit tx.origin = 0xf1585b8d0e08a0a00fff662e24d67ba95a438256 record numbers = 1 name = fruit item_id = 2 item_name = apple1
通过指定外部账号1向t_test表更新记录:
$ ./run.sh 1 1 update fruit 2 apple22 1 1 update fruit 2 apple22 tx.origin = 0xf1585b8d0e08a0a00fff662e24d67ba95a438256 updateCount = 1
t_test表更新记录成功,可以通过查询记录再次验证。
通过指定外部账号1向t_test表删除记录:
$ ./run.sh 1 1 remove fruit 2 1 1 remove fruit 2 tx.origin = 0xf1585b8d0e08a0a00fff662e24d67ba95a438256 removeCount = 1
t_test表删除记录成功,可以通过查询记录再次验证。
外部账号2或3无权限操作t_test表示例:
通过指定外部账号2向t_test表插入记录:
$ ./run.sh 2 1 insert fruit 2 apple2 2 1 insert fruit 2 apple2 tx.origin = 0xc0d0e6ccc0b44c12196266548bec4a3616160e7d insertCount = -1
t_test表插入记录失败,可以通过查询记录再次验证。insertCount返回-1表示无权限插入记录。
通过指定外部账号2向t_test表查询记录:
$ ./run.sh 2 1 read fruit 2 1 read fruit tx.origin = 0xc0d0e6ccc0b44c12196266548bec4a3616160e7d record numbers = 0
通过指定外部账号1向t_test表插入记录:
$ ./run.sh 1 1 insert fruit 2 apple1 1 1 insert fruit 2 apple1 tx.origin = 0xf1585b8d0e08a0a00fff662e24d67ba95a438256 insertCount = 1
t_test表插入记录成功,可以通过查询记录再次验证。外部账号1有权限插入记录。
通过指定外部账号2向t_test表更新记录:
$ ./run.sh 2 1 update fruit 2 apple12 2 1 update fruit 2 apple12 tx.origin = 0xc0d0e6ccc0b44c12196266548bec4a3616160e7d updateCount = -1
t_test表更新记录失败,可以通过查询记录再次验证。updateCount返回-1表示无权限更新记录。
通过指定外部账号2向t_test表删除记录:
$ ./run.sh 2 1 remove fruit 2 2 1 remove fruit 2 tx.origin = 0xc0d0e6ccc0b44c12196266548bec4a3616160e7d removeCount = -1
t_test表删除记录失败,可以通过查询记录再次验证。removeCount返回-1表示无权限删除记录。
4.2.3 移除权限示例¶
登录控制台,移除设置的外部账号1的权限,则外部账号1,2和3均可以对t_test表进行读写操作。
> rum t_test 0xf1585b8d0e08a0a00fff662e24d67ba95a438256
{
"code":1,
"msg":"success"
}
> qum t_test
Empty set.
4.3 节点类型管理控制示例¶
控制台提供5个有关节点类型操作的命令,如下表所示:
命令全称(缩写) | 命令参数 | 含义 |
---|---|---|
addSealer(as) | nodeID | 设置节点为共识节点 |
addObserver(ao) | nodeID | 设置节点为观察节点 |
removeNode(rn) | nodeID | 设置节点为游离节点 |
getSealerList(gsl) | 查询共识节点列表 | |
getObserverList(gol) | 查询观察节点列表 |
注:其中as、ao和rn命令受权限可以控制,gsl和gol命令不受权限控制。
控制台启动时可以指定私钥(从而确定对应的外部账号地址)发送交易,因此通过控制台操作系统表时,可以指定相关外部账号,体验权限控制对系统表的使用。下面提供三个外部账号私钥及其对应的外部账号地址供使用:
账号1:
私钥:3bed914595c159cbce70ec5fb6aff3d6797e0c5ee5a7a9224a21cae8932d84a4
地址: 0xf1585b8d0e08a0a00fff662e24d67ba95a438256
账号2:
私钥:ab40568a2f77b4cb70706b4c6119916a143eb75c0d618e5f69909af1f9f9695e
地址: 0xc0d0e6ccc0b44c12196266548bec4a3616160e7d
账号3:
私钥:d0fee0a4e3c545a9394965042f8f891b6e5482c212a7428ec175d6aed121353a
地址: 0x1600e34312edea101d8b41a3465f2e381b66baed
指定外部账号1登录控制台(其中数字1代表群组1,字符串3bed914595c159cbce70ec5fb6aff3d6797e0c5ee5a7a9224a21cae8932d84a4为外部账号1的私钥)
bash start 1 3bed914595c159cbce70ec5fb6aff3d6797e0c5ee5a7a9224a21cae8932d84a4
4.3.1 默认示例¶
以外部账号1登陆控制台。查看共识节点列表:
> gsl
[
41285429582cbfe6eed501806391d2825894b3696f801e945176c7eb2379a1ecf03b36b027d72f480e89d15bacd43462d87efd09fb0549e0897f850f9eca82ba,
d5b3a9782c6aca271c9642aea391415d8b258e3a6d92082e59cc5b813ca123745440792ae0b29f4962df568f8ad58b75fc7cea495684988e26803c9c5198f3f8,
29c34347a190c1ec0c4507c6eed6a5bcd4d7a8f9f54ef26da616e81185c0af11a8cea4eacb74cf6f61820292b24bc5d9e426af24beda06fbd71c217960c0dff0,
87774114e4a496c68f2482b30d221fa2f7b5278876da72f3d0a75695b81e2591c1939fc0d3fadb15cc359c997bafc9ea6fc37345346acaf40b6042b5831c97e1
]
查看观察节点列表:
> gol
[]
将第一个nodeID对应的节点设置为观察节点:
> ao 41285429582cbfe6eed501806391d2825894b3696f801e945176c7eb2379a1ecf03b36b027d72f480e89d15bacd43462d87efd09fb0549e0897f850f9eca82ba
{
"code":1,
"msg":"success"
}
设置成功。
查看共识节点列表:
> gsl
[
d5b3a9782c6aca271c9642aea391415d8b258e3a6d92082e59cc5b813ca123745440792ae0b29f4962df568f8ad58b75fc7cea495684988e26803c9c5198f3f8,
29c34347a190c1ec0c4507c6eed6a5bcd4d7a8f9f54ef26da616e81185c0af11a8cea4eacb74cf6f61820292b24bc5d9e426af24beda06fbd71c217960c0dff0,
87774114e4a496c68f2482b30d221fa2f7b5278876da72f3d0a75695b81e2591c1939fc0d3fadb15cc359c997bafc9ea6fc37345346acaf40b6042b5831c97e1
]
查看观察节点列表:
> gol
[
41285429582cbfe6eed501806391d2825894b3696f801e945176c7eb2379a1ecf03b36b027d72f480e89d15bacd43462d87efd09fb0549e0897f850f9eca82ba
]
确认设置成功。
将该观察节点设置为共识节点:
> as 41285429582cbfe6eed501806391d2825894b3696f801e945176c7eb2379a1ecf03b36b027d72f480e89d15bacd43462d87efd09fb0549e0897f850f9eca82ba
{
"code":1,
"msg":"success"
}
设置成功。
查看共识节点列表:
> gsl
[
41285429582cbfe6eed501806391d2825894b3696f801e945176c7eb2379a1ecf03b36b027d72f480e89d15bacd43462d87efd09fb0549e0897f850f9eca82ba,
d5b3a9782c6aca271c9642aea391415d8b258e3a6d92082e59cc5b813ca123745440792ae0b29f4962df568f8ad58b75fc7cea495684988e26803c9c5198f3f8,
29c34347a190c1ec0c4507c6eed6a5bcd4d7a8f9f54ef26da616e81185c0af11a8cea4eacb74cf6f61820292b24bc5d9e426af24beda06fbd71c217960c0dff0,
87774114e4a496c68f2482b30d221fa2f7b5278876da72f3d0a75695b81e2591c1939fc0d3fadb15cc359c997bafc9ea6fc37345346acaf40b6042b5831c97e1
]
查看观察节点列表:
> gol
[]
确认设置成功。
类似,可以通过账号2和3登陆控制台均可以执行as、ao和rn命令。
4.3.2 设置权限示例¶
以账号1登录控制台,设置账号1拥有节点管理权限。
> anm 0xf1585b8d0e08a0a00fff662e24d67ba95a438256
{
"code":1,
"msg":"success"
}
> qnm
---------------------------------------------------------------------------------------------
| address | enable_num |
| 0xf1585b8d0e08a0a00fff662e24d67ba95a438256 | 1 |
---------------------------------------------------------------------------------------------
则账号1可以执行as、ao和rn命令,操作见4.3.1。
现在以账号2登陆控制台:
bash start 1 ab40568a2f77b4cb70706b4c6119916a143eb75c0d618e5f69909af1f9f9695e
查看共识节点列表:
> gsl
[
41285429582cbfe6eed501806391d2825894b3696f801e945176c7eb2379a1ecf03b36b027d72f480e89d15bacd43462d87efd09fb0549e0897f850f9eca82ba,
d5b3a9782c6aca271c9642aea391415d8b258e3a6d92082e59cc5b813ca123745440792ae0b29f4962df568f8ad58b75fc7cea495684988e26803c9c5198f3f8,
29c34347a190c1ec0c4507c6eed6a5bcd4d7a8f9f54ef26da616e81185c0af11a8cea4eacb74cf6f61820292b24bc5d9e426af24beda06fbd71c217960c0dff0,
87774114e4a496c68f2482b30d221fa2f7b5278876da72f3d0a75695b81e2591c1939fc0d3fadb15cc359c997bafc9ea6fc37345346acaf40b6042b5831c97e1
]
查看观察节点列表:
> gol
[]
将第一个nodeID对应的节点设置为观察节点:
> ao 41285429582cbfe6eed501806391d2825894b3696f801e945176c7eb2379a1ecf03b36b027d72f480e89d15bacd43462d87efd09fb0549e0897f850f9eca82ba
{
"code":-1,
"msg":"non-authorized"
}
设置失败,提示无权限信息。
查看共识节点列表:
> gsl
[
41285429582cbfe6eed501806391d2825894b3696f801e945176c7eb2379a1ecf03b36b027d72f480e89d15bacd43462d87efd09fb0549e0897f850f9eca82ba,
d5b3a9782c6aca271c9642aea391415d8b258e3a6d92082e59cc5b813ca123745440792ae0b29f4962df568f8ad58b75fc7cea495684988e26803c9c5198f3f8,
29c34347a190c1ec0c4507c6eed6a5bcd4d7a8f9f54ef26da616e81185c0af11a8cea4eacb74cf6f61820292b24bc5d9e426af24beda06fbd71c217960c0dff0,
87774114e4a496c68f2482b30d221fa2f7b5278876da72f3d0a75695b81e2591c1939fc0d3fadb15cc359c997bafc9ea6fc37345346acaf40b6042b5831c97e1
]
查看观察节点列表:
> gol
[]
确认设置失败。
类似以账号3登陆控制台,均无权限执行as、ao和rn命令。
4.3.3 移除权限示例¶
移除外部账号1的权限设置,命令如下:
rnm 0xf1585b8d0e08a0a00fff662e24d67ba95a438256
然后分别再以账号1、2、3对节点类型进行操作。
4.4 CNS控制示例¶
控制台提供3个涉及CNS的命令,如下所示:
命令全称(缩写) | 命令参数 | 功能 |
---|---|---|
deployByCNS(dbc) | contractName contractVersion | 利用CNS部署合约 |
callByCNS(cbc) | contractName contractVersion funcName params | 利用CNS调用合约 |
queryCNS(qcs) | contractName [contractVersion] | 查询CNS信息 |
注:其中dbc命令受权限可以控制,cbc和qcs命令不受权限控制。
4.4.1 默认示例¶
利用CNS部署HelloWorld合约:
> dbc HelloWorld 1.0
0x001815813a1460a0b989935963c27b90d8654216
合约部署成功。查询HelloWorld合约的CNS信息:
> qcs HelloWorld
---------------------------------------------------------------------------------------------
| version | address |
| 1.0 | 0x001815813a1460a0b989935963c27b90d8654216 |
---------------------------------------------------------------------------------------------
确认部署合约的CNS信息写入成功。
类似以账号2和3登陆控制台,均可以利用CNS部署合约。
4.4.2 设置权限示例¶
以账号1登录控制台,设置外部账号1拥有使用CNS的权限。
> acm 0xf1585b8d0e08a0a00fff662e24d67ba95a438256
{
"code":1,
"msg":"success"
}
> qcm
---------------------------------------------------------------------------------------------
| address | enable_num |
| 0xf1585b8d0e08a0a00fff662e24d67ba95a438256 | 2 |
---------------------------------------------------------------------------------------------
以账号2登陆控制台,利用CNS部署合约:
> dbc HelloWorld 3.0
{
"code":-1,
"msg":"non-authorized"
}
部署失败,账号2无权限利用CNS部署合约。类似以账号3登陆控制台也将无权限利用CNS部署合约,以账号1登陆控制台则可以利用CNS部署合约。
4.4.3 移除权限示例¶
移除外部账号1的权限设置,命令如下:
rcm 0xf1585b8d0e08a0a00fff662e24d67ba95a438256
然后分别再以账号1、2、3登陆控制台利用CNS部署合约操作。
4.5 系统参数控制示例¶
控制台提供2个关于系统配置的命令,如下表所示:
命令全称(缩写) | 命令参数 | 功能 |
---|---|---|
setSystemConfigByKey(ssc) | key value | 设置键为key,值为value的系统配置 |
getSystemConfigByKey(gsc) | key | 根据key查询value |
注:目前支持键为tx_count_limit和tx_gas_limit的系统参数设置。其中ssc命令受权限可以控制,gsc命令不受权限控制。
4.5.1 默认示例¶
以账号1登陆控制台,首先查询系统字段tx_count_limit的值:
> gsc tx_count_limit
1000
修改系统字段tx_count_limit的值为2000:
> ssc tx_count_limit 2000
{
"code":1,
"msg":"success"
}
设置成功。查询系统字段tx_count_limit的值:
> gsc tx_count_limit
2000
确认设置成功。
类似以账号2和3登陆控制台,均可以设置系统字段的值。
4.5.2 设置权限示例¶
以账号1登录控制台,设置外部账号1拥有系统参数管理的权限。
> asm 0xf1585b8d0e08a0a00fff662e24d67ba95a438256
{
"code":1,
"msg":"success"
}
> qsm
---------------------------------------------------------------------------------------------
| address | enable_num |
| 0xf1585b8d0e08a0a00fff662e24d67ba95a438256 | 3 |
---------------------------------------------------------------------------------------------
以外部账号2登陆控制台,设置系统字段tx_count_limit的值为3000:
> ssc tx_count_limit 3000
{
"code":-1,
"msg":"non-authorized"
}
设置失败。 查询系统字段tx_count_limit的值:
> gsc tx_count_limit
2000
确认设置失败,账号2无权限修改系统参数。类似账号3登陆控制台也将无权限修改系统参数。
4.5.3 移除权限示例¶
移除外部账号1的权限设置,命令如下:
rsm 0xf1585b8d0e08a0a00fff662e24d67ba95a438256
然后分别再以外部账号1、2、3进行系统参数设置操作。
4.6 权限管理控制示例¶
权利管理功能由权限机制本身控制,如果设置指定外部账号有权限管理功能,则指定的外部账号可以称为管理员账号。管理员账号可以设置其他账号为管理员账号,即设置其他账号是否有权限进行权限功能的设置。
4.6.1 默认示例¶
默认所有外部账号均可以使用权限设置功能。以账号1登陆控制台,设置外部账号1有权限读写t_test表:
> aum t_test 0xf1585b8d0e08a0a00fff662e24d67ba95a438256
{
"code":1,
"msg":"success"
}
设置成功,说明外部账号1有设置权限功能。 查询外部账号1设置的结果:
> qum t_test
---------------------------------------------------------------------------------------------
| address | enable_num |
| 0xf1585b8d0e08a0a00fff662e24d67ba95a438256 | 8 |
---------------------------------------------------------------------------------------------
删除外部账号1设置的结果:
> rum t_test 0xf1585b8d0e08a0a00fff662e24d67ba95a438256
{
"code":1,
"msg":"success"
}
删除成功。类似以账号2和3登陆控制台,均拥有权限设置功能。
4.6.2 设置权限示例¶
设置外部账号1拥有权限设置功能。即账号1成为管理员账号,其他用户为非管理员账号。
> aam 0xf1585b8d0e08a0a00fff662e24d67ba95a438256
{
"code":1,
"msg":"success"
}
> qam
---------------------------------------------------------------------------------------------
| address | enable_num |
| 0xf1585b8d0e08a0a00fff662e24d67ba95a438256 | 7 |
---------------------------------------------------------------------------------------------
外部账号1有权限设置功能示例:设置外部账号1有权限读写t_test表:
> aum t_test 0xf1585b8d0e08a0a00fff662e24d67ba95a438256
{
"code":1,
"msg":"success"
}
设置成功,说明外部账号1有设置权限功能。 查询外部账号1设置的结果:
> qum t_test
---------------------------------------------------------------------------------------------
| address | enable_num |
| 0xf1585b8d0e08a0a00fff662e24d67ba95a438256 | 8 |
---------------------------------------------------------------------------------------------
删除外部账号1设置的结果:
> rum t_test 0xf1585b8d0e08a0a00fff662e24d67ba95a438256
{
"code":1,
"msg":"success"
}
删除成功。
外部账号2无权限设置功能示例:以外部账号2登录控制台,设置外部账号2有权限读写t_test表:
> aum t_test 0xc0d0e6ccc0b44c12196266548bec4a3616160e7d
{
"code":-1,
"msg":"non-authorized"
}
设置失败。查询外部账号2设置的结果:
> qa t_test
Empty set.
查询记录为空,说明外部账号2无设置权限功能。
注: 外部账号3也无设置权限功能,可以类似操作。若需要让外部账号2和3有权限设置功能,可以让账号1登陆控制台,设置账号2和3有权限设置功能。设置命令如下:
aam 0xc0d0e6ccc0b44c12196266548bec4a3616160e7d
aam 0x1600e34312edea101d8b41a3465f2e381b66baed
落盘加密¶
在进行操作前,可先阅读:落盘加密的介绍
部署Key Center¶
每个机构一个Key Center,具体的部署步骤,可参考:keycenter repository
生成节点¶
用build_chain.sh
脚本,用普通的操作方法,先生成节点。
cd FISCO-BCOS/tools
bash build_chain.sh -l "127.0.0.1:4" -p 12300 -e ../build/bin/fisco-bcos
注意:节点生成后,不能启动,待dataKey配置后,再启动。节点在第一次运行前,必须配置好是否采用落盘加密。一旦节点开始运行,无法切换状态。
启动Key Center¶
直接启动keycenter。若未部署keycenter,可参考:keycenter repository
./keycenter 31443 123xyz #参数:端口,superkey
启动成功,打印日志
[1546501342949][TRACE][Load]keycenter stared,port=31443
配置dataKey¶
注意:配置dataKey的节点,必须是新生成,未启动过的节点。
执行脚本,定义dataKey,获取cipherDataKey
cd keycenter/scripts
bash gen_data_secure_key.sh 127.0.0.1 31443 123456
得到cipherDataKey,脚本自动打印出落盘加密需要的ini配置。此时得到节点的cipherDataKey:cipher_data_key=ed157f4588b86d61a2e1745efe71e6ea
[data_secure]
enable=true
keycenter_ip=127.0.0.1
keycenter_port=31443
cipher_data_key=ed157f4588b86d61a2e1745efe71e6ea
将落盘加密的ini配置,写入节点配置文件中
vim nodes/127.0.0.1/node_127.0.0.1_0/config.ini
放在最后即可,如下。
[rpc]
;略
[p2p]
;略
[crl]
;略
[group]
;略
[secure]
;略
[log]
;略
[data_secure]
enable=true
keycenter_ip=127.0.0.1
keycenter_port=31443
cipher_data_key=ed157f4588b86d61a2e1745efe71e6ea
加密节点私钥¶
执行脚本,加密节点私钥
cd keycenter/scripts
bash encrypt_node_key.sh 127.0.0.1 31443 nodes/127.0.0.1/node_127.0.0.1_0/conf/node.key ed157f4588b86d61a2e1745efe71e6ea #参数:ip port 节点私钥文件 cipherDataKey
执行后,节点私钥自动被加密,加密前的文件备份到了文件node.key.bak.xxxxxx
中,请将备份私钥妥善保管,并删除节点上生成的备份私钥
[INFO] File backup to "nodes/127.0.0.1/node_127.0.0.1_0/conf/node.key.bak.1546502474"
[INFO] "nodes/127.0.0.1/node_127.0.0.1_0/conf/node.key" encrypted!
若查看node.key,可看到,已经被加密为密文
8b2eba71821a5eb15b0cbe710e96f23191419784f644389c58e823477cf33bd73a51b6f14af368d4d3ed647d9de6818938ded7b821394446279490b537d04e7a7e87308b66fc82ab3987fb9f3c7079c2477ed4edaf7060ae151f237b466e4f3f8a19be268af65d7b4ae8be37d81810c30a0f00ec7146a1125812989c2205e1e37375bc5e4654e569c21f0f59b3895b137f3ede01714e2312b74918e2501ac6568ffa3c10ae06f7ce1cbb38595b74783af5fea7a8a735309db3e30c383e4ed1abd8ca37e2aa375c913e3d049cb439f01962dd2f24b9e787008c811abd9a92cfb7b6c336ed78d604a3abe3ad859932d84f11506565f733d244f75c9687ef9334b8fabe139a82e9640db9e956b540f8b61675d04c3fb070620c7c135f3f4f6319aae8b6df2b091949a2c9938e5c1e5bb13c0f530764b7c2a884704637be953ce887
注意,所有需要加密的文件列举如下,若未加密,节点无法启动
非国密版:conf/node.key
国密版:conf/gmnode.key 和 conf/origin_cert/node.key
正确性判断¶
(1)节点正常运行,正常出块
(2)keycenter在节点每次启动时,都会打印一条日志。例如,节点在一次启动时,Key Center正确返回的日志如下。
[1546504272699][TRACE][Dec]Respond
{
"dataKey" : "313233343536",
"error" : 0,
"info" : "success"
}
国密使用手册¶
为了充分支持国产密码学算法,金链盟基于国产密码学标准,实现了国密加解密、签名、验签、哈希算法、国密SSL通信协议,并将其集成到FISCO BCOS平台中,实现了对国家密码局认定的商用密码的完全支持。
获取国密二进制¶
$ bash <(curl -s https://raw.githubusercontent.com/FISCO-BCOS/FISCO-BCOS/release-2.0.1/tools/ci/download_bin.sh) -b release-2.0.1 -g
# 执行成功后会在./bin/目录下生成国密版fisco-bcos可执行文件
$ ./bin/fisco-bcos -v
$ FISCO-BCOS gm version 2.0
一键搭链脚本¶
拉取build_chain脚本,并进行安装
$ curl -LO https://raw.githubusercontent.com/FISCO-BCOS/FISCO-BCOS/release-2.0.1/tools/build_chain.sh
$ bash ./build_chain.sh -l '127.0.0.1:4' -e ./bin/fisco-bcos -g
- -e 为编译的国密版fisco-bcos的路径,需要在脚本后指定
- -g 国密编译选项,使用成功后会生成国密版的节点
当国密联盟链部署完成之后,其余操作与快速入门的操作相同
国密配置信息¶
2.0国密版本FISCO BCOS节点之间采用SSL安全通道发送和接收消息,证书主要配置项集中在
[secure] section:
data_path:证书文件所在路径
key:节点私钥相对于data_path的路径
cert: 证书gmnode.crt相对于data_path的路径
ca_cert: gmca证书路径
;certificate configuration
[secure]
;directory the certificates located in
data_path=conf/
;the node private key file
key=gmnode.key
;the node certificate file
cert=gmnode.crt
;the ca certificate file
ca_cert=gmca.crt
conf目录下的original_cert文件夹为节点与sdk进行通信所需要的证书
国密落盘加密配置¶
国密版Key Center¶
国密版的Key Center需重新编译Key Center,不同点在于cmake时带上-DBUILD_GM=ON
选项。
cmake3 .. -DBUILD_GM=On
其它步骤与标准版Key Center相同,请参考:keycenter repository
国密版节点配置¶
国密版的证书有所调整,落盘加密需要加密的证书,变为两个:conf/gmnode.key 和 conf/origin_cert/node.key。其它与标准版落盘加密操作相同。
cd keycenter/scripts
#加密 conf/gmnode.key 参数:ip port 节点私钥文件 cipherDataKey
bash encrypt_node_key.sh 127.0.0.1 31443 nodes/127.0.0.1/node_127.0.0.1_0/conf/gmnode.key ed157f4588b86d61a2e1745efe71e6ea
#加密 conf/origin_cert/node.key 参数:ip port 节点私钥文件 cipherDataKey
bash encrypt_node_key.sh 127.0.0.1 31443 nodes/127.0.0.1/node_127.0.0.1_0/conf/origin_cert/node.key ed157f4588b86d61a2e1745efe71e6ea
日志操作手册¶
FISCO BCOS同时支持轻量级日志系统easylogging++和功能强大的boostlog,默认使用boostlog,可通过编译选项EASYLOG
选择不同的日志系统。
日志配置¶
Important
日志格式:
- boostlog:log_${year}${month}${day}${hour}.log,如log_2018022112.log
- easylog: log_${year}${month}${day}${hour}.${min}.log,如log_2018022112.00.log
配置boostlog¶
FISCO BCOS默认使用boostlog,相较于easylogging++,boostlog配置项很简单,主要如下:
level
: 日志级别,当前主要包括trace、debug、info、warning、error
五种日志级别,设置某种日志级别后,日志文件中会输大于等于该级别的日志,日志级别从大到小排序error > warning > info > debug > trace
;max_log_file_size
:每个日志文件最大容量;flush
:boostlog默认开启日志自动刷新,若需提升系统性能,建议将该值设置为false。
boostlog示例配置如下:
;log configurations
[log]
;the directory of the log
log_path=./log
;log level INFO DEBUG TRACE
level=info
max_log_file_size=209715200
flush=true
配置easylogging++¶
为了尽量减少配置文件,FISCO BCOS将easyloggin++的配置信息都集中到了config.ini的[log]
配置,一般建议不手动更改除了日志级别外的其他配置,开启easylogging++的方法可参考启用easylogging++。
format
:全局日志格式;log_flush_threshold
:日志刷新频率设置,即每log_flush_threshold
行刷新日志到文件一次。
easylogging++示例配置如下:
;log configurations
[log]
;the directory of the log
log_path=./log
;log level INFO DEBUG TRACE
level=info
max_log_file_size=209715200
;easylog config
format=%level|%datetime{%Y-%M-%d %H:%m:%s:%g}|%msg
log_flush_threshold=100
开启boostlog¶
开启boostlog,需将EASYLOG
选项置为OFF
,具体操作如下:
## 进入源码目录,新建build文件夹(设源码位于~/FISCO-BCOS路径)
$ cd ~/FISCO-BCOS && mkdir build
## 关闭EASYLOG编译选项
# ubuntu系统
$ cmake .. -DEASYLOG=OFF
# centos 系统
$ cmake3 .. -DEASYLOG=OFF
------------------------------------------------------------------------
-- Configuring FISCO-BCOS
------------------------------------------------------------------------
-- CMake 3.10.0 (/root/opt/cmake-3.10.0-Linux-x86_64/bin/cmake)
-- CMAKE_BUILD_TYPE Build type RelWithDebInfo
-- TARGET_PLATFORM Target platform Linux
-- STATIC_BUILD Build static OFF
-- ARCH_TYPE OFF
-- TESTS Build tests ON
-- EasyLog Enable easyLog OFF
------------------------------------------------------------------------
-- OpenSSL headers: /usr/include
-- OpenSSL lib : /usr/lib/x86_64-linux-gnu/libssl.so;/usr/lib/x86_64-linux-gnu/libcrypto.so
-- [cable ] Cable 0.2.11 initialized
-- [cable ] Build type: RelWithDebInfo
-- Configuring done
-- Generating done
-- Build files have been written to: /home/FISCO-BCOS/build
# 编译源码并产生二进制(可根据机器硬件配置选择编译并发度,这里设置并发度为2)
$ make -j2
# 查看生成的二进制文件
$ ls bin
fisco-bcos
开启easylogging++¶
开启easylogging++,需将EASYLOG
选项置为ON
,具体操作如下:
## 进入源码目录,新建build文件夹(设源码位于~/FISCO-BCOS路径)
$ cd ~/FISCO-BCOS && mkdir build
## 关闭EASYLOG编译选项
# ubuntu系统
$ cmake .. -DEASYLOG=ON
# centos 系统
$ cmake3 .. -DEASYLOG=ON
------------------------------------------------------------------------
-- Configuring FISCO-BCOS
------------------------------------------------------------------------
-- CMake 3.10.0 (/root/opt/cmake-3.10.0-Linux-x86_64/bin/cmake)
-- CMAKE_BUILD_TYPE Build type RelWithDebInfo
-- TARGET_PLATFORM Target platform Linux
-- STATIC_BUILD Build static OFF
-- ARCH_TYPE OFF
-- TESTS Build tests ON
-- EasyLog Enable easyLog ON
------------------------------------------------------------------------
-- OpenSSL headers: /usr/include
-- OpenSSL lib : /usr/lib/x86_64-linux-gnu/libssl.so;/usr/lib/x86_64-linux-gnu/libcrypto.so
-- [cable ] Cable 0.2.11 initialized
-- [cable ] Build type: RelWithDebInfo
-- Configuring done
-- Generating done
-- Build files have been written to: /home/FISCO-BCOS/build
# 编译源码并产生二进制(可根据机器硬件配置选择编译并发度,这里设置并发度设置为2)
$ make -j2
# 查看生成的二进制文件
$ ls bin
fisco-bcos
系统设计¶
本章介绍FISCO BCOS平台的设计思路,包括每个模块的结构以及实现,面向FISCO BCOS平台开发者。
整体架构¶
整体架构上,FISCO BCOS划分成基础层、核心层、管理层和接口层:
- 基础层:提供区块链的基础数据结构和算法库
- 核心层: 实现了区块链的核心逻辑,核心层分为两大部分:
- 链核心层: 实现区块链的链式数据结构、交易执行引擎和存储驱动
- 互联核心层: 实现区块链的基础P2P网络通信、共识机制和区块同步机制
- 管理层: 实现区块链的管理功能,包括参数配置、账本管理和AMOP
- 接口层: 面向区块链用户,提供多种协议的RPC接口、SDK和交互式控制台
FISCO BCOS基于多群组架构实现了强扩展性的群组多账本,基于清晰的模块设计,构建了稳定、健壮的区块系统。
本章重点介绍FISCO BCOS的群组架构和系统运行时的交易流(包括交易提交、打包、执行和上链)。

群组架构¶
考虑到真实的业务场景需求,FISCO BCOS引入多群组架构,支持区块链节点启动多个群组,群组间交易处理、数据存储、区块共识相互隔离,保障区块链系统隐私性的同时,降低了系统的运维复杂度。
Important
举个栗子:
机构A、B、C所有节点构成一个区块链网络,运行业务1;一段时间后,机构A、B启动业务2,且不希望该业务相关数据、交易处理被机构C感知,有何解?
- 1.3系列FISCO BCOS系统 :机构A和机构B重新搭一条链运行业务2;运维管理员需要运维两条链,维护两套端口
- FISCO BCOS 2.0 :机构A和机构B新建一个群组运行业务2;运维管理员仅需维护一条链
显然在达到相同隐私保护需求基础上,FISCO BCOS 2.0具有更好的扩展性、可运维性和灵活性。
举个栗子:
机构A、B、C所有节点构成一个区块链网络,运行业务1;
一段时间后,机构A、B启动业务2,且不希望该业务相关数据、交易处理被机构C感知,有和解?
多群组架构中,群组间共享网络,通过网络准入和账本白名单实现各账本间网络消息隔离。
群组间数据隔离,每个群组独立运行各自的共识算法,不同群组可使用不同的共识算法。每个账本模块自底向上主要包括核心层、接口层和调度层三层,这三层相互协作,FISCO BCOS可保证单个群组独立健壮地运行。
核心层¶
核心层负责将群组的区块数据、区块信息、系统表以及区块执行结果写入底层数据库。
存储分为state和AMDB两部分,state负责存储合约执行的状态信息,可追踪交易历史状态;AMDB则负责将区块数据、区块信息、系统参数等写入系统表中。
AMDB向外暴露简单的查询(select)、提交(commit)和更新(update)接口,具有可插拔特性,后端可支持多种数据库类型,目前仅支持LevelDB数据库,后期会把基于mysql的AMDB集成到系统中。
接口层¶
接口层包括交易池(TxPool)、区块链(BlockChain)和区块执行器(BlockVerifier)三个模块。
- 交易池(TxPool): 与网络层以及调度层交互,负责缓存客户端或者其他节点广播的交易,调度层(主要是同步和共识模块)从交易池中取出交易进行广播或者区块打包;
- 区块链(BlockChain): 与核心层和调度层交互,是调度层访问底层存储的唯一入口,调度层(同步、共识模块)可通过区块链接口查询块高、获取指定区块、提交区块;
- 区块执行器(BlockVerifier): 与调度层交互,负责执行从调度层传入的区块,并将区块执行结果返回给调度层。
调度层¶
调度层包括共识模块(Consensus)和同步模块(Sync)。
- 共识模块:包括Sealer线程和Engine线程,分别负责打包交易、执行共识流程。Sealer线程从交易池(TxPool)取交易,并打包成新区块;Engine线程执行共识流程,共识过程会执行区块,共识成功后,将区块以及区块执行结果提交到区块链(BlockChain),区块链统一将这些信息写入底层存储,并触发交易池删除上链区块中包含的所有交易、将交易执行结果以回调的形式通知客户端,目前FISCO BCOS主要支持PBFT和Raft共识算法;
- 同步模块:负责广播交易和获取最新区块, 考虑到共识过程中,leader负责打包区块,而leader随时有可能切换,因此必须保证客户端的交易尽可能发送到每个区块链节点,节点收到新交易后,同步模块将这些新交易广播给所有其他节点;考虑到区块链网络中机器性能不一致或者新节点加入都会导致部分节点区块高度落后于其他节点,同步模块提供了区块同步功能,该模块向其他节点发送自己节点的最新块高,其他节点发现块高落后于其他节点后,会主动下载最新区块。
交易流¶
1 总体方案¶
用户通过SDK或Curl命令向节点发起RPC请求以发起交易,节点收到交易后将交易附加到交易池中,打包器不断从交易池中取出交易并通过一定条件触发将取出交易打包为区块。生成区块后,由共识引擎进行验证及共识,验证区块无误且节点间达成共识后,将区块上链。当节点通过同步模块从其他节点处下载缺失的区块时,会同样对区块进行执行及验证。
2 整体架构¶
整体架构如下图所示:
Node:区块节点
TxPool:交易池,节点自身维护的、用于暂存收到的交易的内存区域
Sealer:打包器
Consensus Engine:共识引擎
BlockVerifier:区块验证器,用于验证一个区块的正确性
Executor:执行引擎,执行单个交易
BlockChain:区块链管理模块,是唯一有写权限的模块,提交区块接口需要同时传入区块数据和执行上下文数据,区块链管理将两种数据整合成一个事务提交到底层分布式存储
Storage:底层分布式存储
主要关系如下:
- 用户通过操作SDK或直接编写Curl命令向所连接的节点发起交易。
- 节点收到交易后,若当前交易池未满则将交易附加至TxPool中并向自己所连的节点广播该交易;否则丢弃交易并输出告警。
- Sealer会不断从交易池中取出交易,当满足下列任意一个条件——(1) 取出的交易数量已经达到区块允许的最大交易数量;(2) 距离上次的出块时间已经达到最大出块时间间隔——时,Sealer将收集到的交易打包为区块并发送至共识引擎。
- 共识引擎调用BlockVerifier对区块进行验证并在网络中进行共识,BlockVerifier调用Executor执行区块中的每笔交易。当区块验证无误且网络中节点达成一致后,共识引擎将区块发送至BlockChain。
- BlockChain收到区块,对区块信息(如块高等)进行检查,并将区块数据与表数据写入底层分布式存储中,完成区块上链。
3 方案流程¶
3.1 合约执行流程¶
执行引擎基于执行上下文(Executive Context)执行单个交易,其中执行上下文由区块验证器创建用于缓存暂存区块执行过程中执行引擎产生的所有数据,执行引擎同时支持EVM合约与Precompiled合约,其中EVM合约可以通过交易创建合约、合约创建合约两种方式来创建,其执行流程如下:
EVM合约创建后,保存到执行上下文的_sys_contracts_表中,EVM合约的地址在区块链全局状态内自增,从0x1000001开始(可定制),EVM合约执行过程中,Storage变量保存到执行上下文的_contract_data_(合约地址)_表中。
Precompiled合约分永久和临时两种:(1) 永久Precompiled合约,整合在底层或插件中,合约地址固定;(2) 临时Precompiled合约,EVM合约或Precompiled合约执行时动态创建,合约地址在执行上下文内自增,从0x1000开始,至0x1000000截止,临时Precompiled合约仅在执行上下文内有效Precompiled合约没有Storage变量,只能操作表,其执行流程如下:
3.2 区块验证到提交的过程¶
P2P网络¶
设计目标¶
FISCO-BCOS P2P模块提供高效、通用和安全的网络通信基础功能,支持区块链消息的单播、组播和广播,支持区块链节点状态同步,支持多种协议。
P2P主要功能¶
- 区块链节点标识
通过区块链节点标识唯一标识一个区块链节点,在区块链网络上通过区块链节点标识对区块链节点进行寻址
- 管理网络连接
维持区块链网络上区块链节点间的TCP长连接,自动断开异常连接,自动发起重连
- 消息收发
在区块链网络的区块链节点间,进行消息的单播、组播或广播
- 状态同步
在区块链节点间同步状态
区块链节点标识¶
区块链节点标识由ECC算法的公钥生成,每个区块链节点必须有唯一的ECC密钥对,区块链节点标识在区块链网络中唯一标识一个区块链节点
通常情况下,一个节点要加入区块链网络,至少要准备三个文件:
- node.key 节点密钥,ECC格式
- node.crt 节点证书,由CA颁发
- ca.crt CA证书,CA机构提供
区块链节点除了有唯一区块链节点标识,还能关注Topic,供寻址使用
区块链节点寻址:
- 区块链节点标识寻址
通过区块链节点标识,在区块链网络中定位唯一的区块链节点
- Topic寻址
通过Topic,在区块链网络中定位一组关注该Topic的节点
同步¶
共识,是区块链节点的核心功能,共识保证了区块链上的节点的一致性。共识,通过将交易打包成区块,再用共识算法,确保每个节点上的区块是一致且正确的,并最在每个节点上都保存一条相同的区块链。
同步,也是区块链节点非常重要的功能。它是共识的辅助,给共识提供必需的运行条件。同步分为交易的同步和状态的同步。交易的同步,确保了每笔交易能正确的到达每个节点上。状态的同步,能确保区块落后的节点能正确的回到最新的状态。只有持有最新区块状态的节点,才能参与到共识中去。
交易同步¶
交易同步,是让区块链的上的交易尽可能的到达所有的节点。为共识中将交易打包成区块提供基础。
一笔交易(tx1),从SDK上发往某个节点,节点在接收到交易后,会将交易放入自身的交易池(Tx Pool)中,供共识去打包。与此同时,节点会将交易广播给其它的节点,其它节点收到交易后,也会将交易放到自身的交易池中。交易在发送的过程中,会有丢失的情况,为了能让交易尽可能的到达所有的节点,收到广播过来交易的节点,会根据一定的策略,选择其它的节点,再进行一次广播。
交易广播策略
如果每个节点都没有限制的转发/广播收到的交易,带宽将被占满,出现交易广播雪崩的问题。为了避免交易广播的雪崩,FISCO BCOS根据经验,选择了较为精巧的交易广播策略。在尽可能保证交易可达性的前提下,尽量的减少重复的交易广播。
- 对于SDK来的交易,广播给所有的节点
- 对于其它节点广播来的交易,随机选择25%的节点再次广播
- 一条交易在一个节点上,只广播一次,当收到了重复的交易,不会进行二次广播
通过上述的策略,能够尽量的让交易到达所有的节点,但也会在极小的概率下出现某交易无法到达某节点的情况。此情况是允许的。交易尽可能到达更多的节点,是为了让此交易尽快的被打包、共识、确认,尽量的让交易能够更快的得到执行的结果。当交易未到达某个节点时,只会使得交易的执行时间变长,不会影响交易的正确性。
状态同步¶
状态同步,是让区块链节点的状态保持在最新。区块链的状态的新旧,是指区块链节点当前持有数据的新旧,即节点持有的当前区块块高的高低。若一个节点的块高是区块链的最高块高,则此节点就拥有区块链的最新状态。只有拥有最新状态的节点,才能参与到共识中去,进行下一个新区块的共识。
在一个全新的节点加入到区块链上,或一个已经断网的节点恢复了网络时,此节点的区块落后于其它节点,状态不是最新的。此时就需要进行状态同步。如图,需要状态同步的节点(Node 1),会主动向其它节点请求下载区块。整个下载的过程会将下载的负载分散到多个节点上。
状态同步与下载队列
区块链节点在运行时,会定时向其它节点广播自身的最高块高。节点收到其它节点广播过来的块高后,会和自身的块高进行比较,若自身的块高落后于此块高,就会启动区块下载流程。
区块的下载通过请求的方式完成。进入下载流程的节点,会随机的挑选满足要求的节点,发送需要下载的区块区间。收到下载请求的节点,会根据请求的内容,回复相应的区块。
收到回复区块的节点,在本地维护一个下载队列,用来对下载下来的区块进行缓冲和排序。下载队列是一个以块高为顺序的优先队列。下载下来的区块,会不断的插入到下载队列中,当队列中的区块链能连接上节点当前本地的区块链,则将区块从下载队列中取出,真正的连接到当前本地的区块链上。
共识算法¶
区块链系统通过共识算法保障系统一致性。 理论上,共识是对某个提案(proposal)达成一致意见的过程,分布式系统中提案的含义十分宽泛,包括事件发生顺序、谁是leader等。区块链系统中,共识是各个出块节点对交易执行结果达成一致的过程。
共识算法分类
根据是否容忍 拜占庭错误 ,共识算法可分为容错(Crash Fault Tolerance, CFT)类算法和拜占庭容错(Byzantine Fault Tolerance, BFT)类算法:
- CFT类算法 :普通容错类算法,当系统出现网络、磁盘故障,服务器宕机等普通故障时,仍能针对某个提议达成共识,经典的算法包括Paxos、Raft等,这类算法性能较好、处理速度较快、可以容忍不超过一半的故障节点;
- BFT类算法 :拜占庭容错类算法,除了容忍系统共识过程中出现的普通故障外、还可容忍部分节点故意欺骗(如伪造交易执行结果)等拜占庭错误,经典算法包括PBFT等,这类算法性能较差,能容忍不超过三分之一的故障节点。
FISCO BCOS共识算法
FISCO BCOS基于多群组架构实现了插件化的共识算法,不同群组可运行不同的共识算法,组与组之间的共识过程互不影响,FISCO BCOS目前支持PBFT(Practical Byzantine Fault Tolerance)和Raft(Replication and Fault Tolerant)两种共识算法:
- PBFT共识算法: BFT类算法,可容忍不超过三分之一的故障节点和作恶节点,可达到最终一致性;
- Raft共识算法: CFT类算法, 可容忍一半故障节点,不能防止节点作恶,可达到一致性。
框架¶
FISCO BCOS实现了一套可扩展的共识框架,可插件化扩展不同的共识算法,目前支持 PBFT(Practical Byzantine Fault Tolerance) 和 RAFT(Replication and Fault Tolerant) 共识算法,共识模块框架如下图:
Sealer线程
交易打包线程,负责从交易池取交易,并基于节点最高块打包交易,产生新区块,产生的新区块交给Engine线程处理,PBFT和Raft的交易打包线程分别为PBFTSealer和RaftSealer。
Engine线程
共识线程,负责从本地或通过网络接收新区块,并根据接收的共识消息包完成共识流程,最终将达成共识的新区块写入区块链(BlockChain),区块上链后,从交易池中删除已经上链的交易,PBFT和Raft的共识线程分别为PBFTEngine和RaftEngine。
PBFT¶
PBFT(Practical Byzantine Fault Tolerance)共识算法可以在少数节点作恶(如伪造消息)场景中达成共识,它采用签名、签名验证、哈希等密码学算法确保消息传递过程中的防篡改性、防伪造性、不可抵赖性,并优化了前人工作,将拜占庭容错算法复杂度从指数级降低到多项式级别,在一个由(3*f+1)个节点构成的系统中,只要有不少于(2*f+1)个非恶意节点正常工作,该系统就能达成一致性,如:7个节点的系统中允许2个节点出现拜占庭错误。
FISCO BCOS区块链系统实现了PBFT共识算法。
1. 重要概念¶
节点类型、节点ID、节点索引和视图是PBFT共识算法的关键概念。区块链系统基本概念请参考关键概念。
1.1 节点类型¶
- Leader/Primary: 出块节点,负责将交易打包成区块和区块共识,每轮共识过程中有且仅有一个leader,为了防止leader伪造区块,每轮PBFT共识后,均会切换leader;
- Replica: 副本节点,负责区块共识,每轮共识过程中有多个Replica节点,每个Replica节点的处理过程类似;
- Observer: 观察者节点,负责从出块节点或副本节点获取最新区块,执行并验证区块执行结果后,将产生的区块上链。
其中Leader和Replica统称为共识节点。
1.2 节点ID && 节点索引¶
为了防止节点作恶,PBFT共识过程中每个共识节点均对其发送的消息进行签名,对收到的消息包进行验签名,因此每个节点均维护一份公私钥对,私钥用于对发送的消息进行签名,公钥作为节点ID,用于标识和验签。
节点ID : 共识节点签名公钥和共识节点唯一标识, 一般是64字节二进制串,其他节点使用消息包发送者的节点ID对消息包进行验签
考虑到节点ID很长,在共识消息中包含该字段会耗费部分网络带宽,FISCO BCOS引入了节点索引,每个共识节点维护一份公共的共识节点列表,节点索引记录了每个共识节点ID在这个列表中的位置,发送网络消息包时,只需要带上节点索引,其他节点即可以从公共的共识节点列表中索引出节点的ID,进而对消息进行验签:
节点索引 : 每个共识节点ID在这个公共节点ID列表中的位置
1.3 视图(view)¶
PBFT共识算法使用视图view记录每个节点的共识状态,相同视图节点维护相同的Leader和Replicas节点列表。当Leader出现故障,会发生视图切换,若视图切换成功(至少2*f+1个节点达到相同视图),则根据新的视图选出新leader,新leader开始出块,否则继续进行视图切换,直至全网大部分节点(大于等于2*f+1)达到一致视图。
FISCO-BCOS系统中,leader索引的计算公式如下:
leader_idx = (view + block_number) % node_num
下图简单展示了4(3*1+1, f=1)
节点FISCO BCOS系统中,第三个节点(node3)为拜占庭节点情况下,视图切换过程:
- 前三轮共识: node0、node1、node2为leader,且非恶意节点数目等于
2*f+1
,节点正常出块共识; - 第四轮共识:node3为leader,但node3为拜占庭节点,node0-node2在给定时间内未收到node3打包的区块,触发视图切换,试图切换到
view_new=view+1
的新视图,并相互间广播viewchange包,节点收集满在视图view_new
上的(2*f+1)
个viewchange包后,将自己的view切换为view_new
,并计算出新leader; - 为第五轮共识:node0为leader,继续打包出块。
1.4 共识消息¶
PBFT模块主要包括PrepareReq、SignReq、CommitReq和ViewChangeReq四种共识消息:
- PrepareReqPacket: 包含区块的请求包,由leader产生并向所有Replica节点广播,Replica节点收到Prepare包后,验证PrepareReq签名、执行区块并缓存区块执行结果,达到防止拜占庭节点作恶、保证区块执行结果的最终确定性的目的;
- SignReqPacket: 带有区块执行结果的签名请求,由收到Prepare包并执行完区块的共识节点产生,SignReq请求带有执行后区块的hash以及该hash的签名,分别记为SignReq.block_hash和SignReq.sig,节点将SignReq广播到所有其他共识节点后,其他节点对SignReq(即区块执行结果)进行共识;
- CommitReqPacket: 用于确认区块执行结果的提交请求,由收集满
(2*f+1)
个block_hash相同且来自不同节点SignReq请求的节点产生,CommitReq被广播给所有其他共识节点,其他节点收集满(2*f+1)
个block_hash相同、来自不同节点的CommitReq后,将本地节点缓存的最新区块上链; - ViewChangeReqPacket: 视图切换请求,当leader无法提供正常服务(如网络连接不正常、服务器宕机等)时, 其他共识节点会主动触发视图切换,ViewChangeReq中带有该节点即将切换到的视图(记为toView,为当前视图加一),某节点收集满
(2*f+1)
个视图等于toView、来自不同节点的ViewChangeReq后,会将当前视图切换为toView。
这四类消息包包含的字段大致相同,所有消息包共有的字段如下:
字段名 | 字段含义 |
---|---|
字段名 | 字段含义 |
idx | 当前节点索引 |
packetType | 请求包类型(包括PrepareReqPacket/SignReqPacket/CommitReqPacket/ViewChangeReqPacket) |
height | 当前正在处理的区块高度(一般是本地区块高度加一) |
blockHash | 当前正在处理的区块哈希 |
view | 当前节点所处的视图 |
sig | 当前节点对blockHash的签名 |
PrepareReqPacket类型消息包包含了正在处理的区块信息:
消息包类型 | 字段名 | 字段含义 |
---|---|---|
PrepareReqPacket | block | 所有共识节点正在共识的区块数据 |
2. 系统框架¶
系统框架如下图:
PBFT共识主要包括两个线程:
- PBFTSealer: PBFT打包线程,负责从交易池取交易,并将打包好的区块封装成PBFT Prepare包,交给PBFTEngine处理;
- PBFTEngine: PBFT共识线程,从PBFTSealer或者P2P网络接收PBFT共识消息包,完成共识流程,将达成共识的区块写入区块链,区块上链后,从交易池中删除已经上链的交易,区块验证器(Blockverifier)负责执行区块。
3. 核心流程¶
PBFT共识主要包括Pre-parepare、Prepare和Commit三个阶段:
- Pre-prepare:负责执行区块,产生签名包,并将签名包广播给所有共识节点;
- Prepare:负责收集签名包,某节点收集满
2*f+1
的签名包后,表明自身达到可以提交区块的状态,开始广播Commit包; - Commit:负责收集Commit包,某节点收集满
2*f+1
的Commit包后,直接将本地缓存的最新区块提交到数据库。
下图详细介绍了PBFT各个阶段的具体流程:
3.1 leader打包区块¶
PBFT共识算法中,共识节点轮流出块,每一轮共识仅有一个leader打包区块,leader索引通过公式(block_number + current_view) % consensus_node_num
计算得出。
节点计算当前leader索引与自己索引相同后,就开始打包区块。区块打包主要由PBFTSealer线程完成,Sealer线程的主要工作如下图所示:
- 产生新的空块: 通过区块链(BlockChain)获取当前最高块,并基于最高块产生新空块(将新区块父哈希置为最高块哈希,时间戳置为当前时间,交易清空);
- 从交易池打包交易: 产生新空块后,从交易池中获取交易,并将获取的交易插入到产生的新区块中;
- 组装新区块: Sealer线程打包到交易后,将新区块的打包者(Sealer字段)置为自己索引,并根据打包的交易计算出所有交易的transactionRoot;
- 产生Prepare包: 将组装的新区块编码到Prepare包内,通过PBFTEngine线程广播给组内所有共识节点,其他共识节点收到Prepare包后,开始进行三阶段共识。
3.2 pre-prepare阶段¶
共识节点收到Prepare包后,进入pre-prepare阶段,此阶段的主要工作流程包括:
- Prepare包合法性判断:主要判断是否是重复的Prepare包、Prepare请求中包含的区块父哈希是否是当前节点最高块哈希(防止分叉)、Prepare请求中包含区块的块高是否等于最高块高加一;
- 缓存合法的Prepare包: 若Prepare请求合法,则将其缓存到本地,用于过滤重复的Prepare请求;
- 空块判断:若Prepare请求包含的区块中交易数目是0,则触发空块视图切换,将当前视图加一,并向所有其他节点广播视图切换请求;
- 执行区块并缓存区块执行结果: 若Prepare请求包含的区块中交易数目大于0,则调用BlockVerifier区块执行器执行区块,并缓存执行后的区块;
- 产生并广播签名包:基于执行后的区块哈希,产生并广播签名包,表明本节点已经完成区块执行和验证。
3.3 Prepare阶段¶
共识节点收到签名包后,进入Prepare阶段,此阶段的主要工作流程包括:
- 签名包合法性判断:主要判断签名包的哈希与pre-prepare阶段缓存的执行后的区块哈希相同,若不相同,则继续判断该请求是否属于未来块签名请求(产生未来块的原因是本节点处理性能低于其他节点,还在进行上一轮共识,判断未来块的条件是:签名包的height字段大于本地最高块高加一),若请求也非未来块,则是非法的签名请求,节点直接拒绝该签名请求;
- 缓存合法的签名包:节点会缓存合法的签名包;
- 判断pre-prepare阶段缓存的区块对应的签名包缓存是否达到
2*f+1
,若收集满签名包,广播Commit包:若pre-prepare阶段缓存的区块哈希对应的签名包数目超过2*f+1
,则说明大多数节点均执行了该区块,并且执行结果一致,说明本节点已经达到可以提交区块的状态,开始广播Commit包; - 若收集满签名包,备份pre-prepare阶段缓存的Prepare包落盘:为了防止Commit阶段区块未提交到数据库之前超过
2*f+1
个节点宕机,这些节点启动后重新出块,导致区块链分叉(剩余的节点最新区块与这些节点最高区块不同),还需要备份pre-prepare阶段缓存的Prepare包到数据库,节点重启后,优先处理备份的Prepare包。
3.4 Commit阶段¶
共识节点收到Commit包后,进入Commit阶段,此阶段工作流程包括:
- Commit包合法性判断:主要判断Commit包的哈希与pre-prepare阶段缓存的执行后的区块哈希相同,若不相同,则继续判断该请求是否属于未来块Commit请求(产生未来块的原因是本节点处理性能低于其他节点,还在进行上一轮共识,判断未来块的条件是:Commit的height字段大于本地最高块高加一),若请求也非未来块,则是非法的Commit请求,节点直接拒绝该请求;
- 缓存合法的Commit包:节点缓存合法的Commit包;
- 判断pre-prepare阶段缓存的区块对应的Commit包缓存是否达到
2*f+1
,若收集满Commit包,则将新区块落盘:若pre-prepare阶段缓存的区块哈希对应的Commit请求数目超过2*f+1
,则说明大多数节点达到了可提交该区块状态,且执行结果一致,则调用BlockChain模块将pre-prepare阶段缓存的区块写入数据库;
3.5 视图切换处理流程¶
当PBFT三阶段共识超时或节点收到空块时,PBFTEngine会试图切换到更高的视图(将要切换到的视图toView加一),并触发ViewChange处理流程;节点收到ViewChange包时,也会触发ViewChange处理流程:
- 判断ViewChange包是否有效: 有效的ViewChange请求中带有的块高值必须不小于当前节点最高块高,视图必须大于当前节点视图;
- 缓存有效ViewChange包: 防止相同的ViewChange请求被处理多次,也作为判断节点是否可以切换视图的统计依据;
- 收集ViewChange包:若收到的ViewChange包中附带的view等于本节点的即将切换到的视图toView且本节点收集满
2*f+1
来自不同节点view等于toView的ViewChange包,则说明超过三分之二的节点要切换到toView视图,切换当前视图到toView,否则若至少有三分之一的节点达到其他视图,则将本节点视图切换到这些节点的视图。
RAFT¶
1 名词解释¶
1.1 Raft¶
Raft(Replication and Fault Tolerant)是一个允许网络分区(Partition Tolerant)的一致性协议,它保证了在一个由N个节点构成的系统中有(N+1)/2(向上取整)个节点正常工作的情况下的系统的一致性,比如在一个5个节点的系统中允许2个节点出现非拜占庭错误,如节点宕机、网络分区、消息延时。Raft相比于Paxos更容易理解,且被证明可以提供与Paxos相同的容错性以及性能,其详细介绍可见官网及动态演示。
1.2 节点类型¶
在Raft算法中,每个网络节点只能如下三种身份之一:Leader、Follower以及Candidate,其中:
- Leader:主要负责与外界交互,由Follower节点选举而来,在每一次共识过程中有且仅有一个Leader节点,由Leader全权负责从交易池中取出交易、打包交易组成区块并将区块上链;
- Follower:以Leader节点为准进行同步,并在Leader节点失效时举行选举以选出新的Leader节点;
- Candidate:Follower节点在竞选Leader时拥有的临时身份。
1.3 节点ID & 节点索引¶
在Raft算法中,每个网络节点都会有一个固定且全局的唯一的用于表明节点身份的ID(一般是一个64字节表示数字),这成为节点ID;同时每个共识节点还会维护一份公共的共识节点列表,这个列表记录了每个共识节点的ID,而自己在这个列表中的位置被称为节点索引。
1.3 任期¶
Raft算法将时间划分为不定长度的任期Terms,Terms为连续的数字。每个Term以选举开始,如果选举成功,则由当前leader负责出块,如果选举失败,并没有选举出新的单一Leader,则会开启新的Term,重新开始选举。
.
1.4 消息¶
在Raft算法中,每个网络节点间通过发送消息进行通讯,当前Raft模块包括四种消息:VoteReq、VoteResp、Heartbeat、HeartbeatResp,其中:
- VoteReq:投票请求,由Candidate节点主动发出,用于向网络中其他节点请求投票以竞选Leader;
- VoteResp:投票响应,在节点收到投票请求后,用于对投票请求进行响应,响应内容为同意或拒绝该投票请求;
- Heartbeat:心跳,由Leader节点主动周期发出,其作用有两个:(1) 用于维护Leader节点身份,只要Leader能够一直正常发送心跳且被其他节点响应,Leader身份就不会发生变化;(2) 区块数据复制,当Leader节点成功打包一个区块后,会将区块数据编码至心跳中以将区块进行广播,其他节点在收到该心跳后会解码出区块数据并将区块放入自己的缓冲区中;
- Heartbeat:心跳响应,在节点收到心跳后后,用于对心跳进行效应,特别的,当收到一个包含区块数据的心跳时,该心跳的响应中会带上该区块的哈希;
所有消息共有的字段如下表所示:
字段名 | 字段含义 |
---|---|
idx | 自身节点索引 |
term | 前节点所处在的任期 |
height | 当前节点所持有的最高块的块高 |
blockHash | 前节点所持有的最高块的哈希 |
每种消息类型特有的字段如下表所示:
消息类型 | 字段名 | 字段含义 |
VoteReq | candidate | Candidate自身的节点索引 |
lastLeaderTerm | Candidate见到过的最后一个Leader的Term,其详细作用见3.1.2节 | |
lastBlockNumber | Candidate见到过的最新块的块高,其详细作用见3.1.2节 | |
VoteResp | voteFlag | 对投票请求的响应标志位,用以标记对是否同意投票请求,若是拒绝还会具体标记拒绝原因,其详细作用见3.1.2节 |
lastLeaderTerm | 收到VoteReq的节点见到过的最新块的块高,其详细作用见3.1.2节 | |
Heartbeat | leader | 发出心跳的Leader节点的节点索引 |
uncommitedBlock | 当Leader节点预备提交一个新块时,会先将区块数据编码进此字段以并通过心跳进行广播,其详细作用见3.2节 | |
uncommitedBlockNumber | uncommitedBlock对应的块高,其详细作用见3.2节 | |
HeartbeatResp | uncommitedBlockHash | 当收到Leader发送过来的uncommitedBlock数据时,节点在心跳响应中写入uncommitedBlock对应的哈希(指纹),并发送回Leader,表明节点已经收到Leader预备提交的区块数据且已写入本地缓存,其详细作用见3.2节 |
2 系统框架¶
系统框架如下图所示:
- Raft Sealer:负责从交易池取出交易并打包成区块,并发送至Raft Engine进行共识。区块上链后,Raft Sealer负责从交易池中删除已上链交易;
- Raft Engine:负责在共识节点进行共识,将达成共识的区块上链。
3 核心流程¶
3.1 节点状态转换¶
节点类型之间转换关系如下图所示,每种状态转换形式将在接下来的各个小节进行阐述:
Raft共识模块中使用心跳机制来触发Leader选举。当节点启动时,节点自动成为Follower且将Term置0。只要Follower从Leader或者Candidate收到有效的Heartbeat或RequestVote消息,其就会保持在Follower状态,如果Follower在一段时间内(这段时间称为 Election Timeout)没收到上述消息,则它会假设系统当前的Leader已经失活,然后增加自己的Term并转换为Candidiate,开启新一轮的Leader选举流程,流程如下:
- Follower增加当前的Term,转换为Candidate;
- Candidate将票投给自己,并广播RequestVote到其他节点请求投票;
- Candidate节点保持在Candidate状态,直到下面三种情况中的一种发生:(1)该节点赢得选举;(2) 在等待选举期间,Candidate收到了其他节点的Heartbeat;(3) 经过Election Timeout后,没有Leader被选出。Raft算法采用随机定时器的方法来避免节点选票出现平均瓜分的情况以保证大多数时候只会有一个节点超时进入Candidate状态并获得大部分节点的投票成为Leader。
节点在收到VoteReq消息后,会根据消息的内容选择不同的响应策略:
VoteReq的Term小于或等于自己的Term
- 如果节点是Leader,则拒绝该投票请求,Candidate收到此响应后会放弃选举转变为Follower,并增加投票超时;
- 如果节点不是Leader:
- 如果VoteReq的Term小于自己的Term,则拒绝该投票请求,如果Candidate收到超过半数的该种响应则表明其已经过时,此时Candidate会放弃选举转变为Follower,并增加投票超时;
- 如果VoteReq的Term等于自己的Term,则拒绝该投票请求,对于该投票请求不作任何处理。对于每个节点而言,只能按照先到先得的原则投票给一个Candidate,从而保证每轮选举中至多只有一个Candidate被选为Leader。
VoteReq的lastLeaderTerm小于自己的lastLeaderTerm
每个节点中会有一个lastLeaderTerm字段表示该节点见过的最后一个Leader的Term,lastLeaderTerm仅能由Heartbeat进行更新。如果VoteReq中的lastLeaderTerm小于自己的lastLeaderTerm,表明Leader访问这个Candidate存在问题,如果此时Candidate处于网络孤岛的环境中,会不断向外提起投票请求,因此需要打断它的投票请求,所以此时节点会拒绝该投票请求。
VoteReq的lastBlockNumber小于自己的lastBlockNumber
每个节点中会有一个lastBlockNumber字段表示节点见到过的最新块的块高。在出块过程中,节点间会进行区块复制(详见3.2节),在区块复制的过程中,可能有部分节点收到了较新的区块数据而部分没有,从而导致不同节点的lastBlockNumber不一致。为了使系统能够达成一致,需要要求节点必须把票投给拥有较新数据的节点,因此在这种情况下节点会拒绝该投票请求。
节点是第一次投票
为了避免出现Follower因为网络抖动导致重新发起选举,规定如果节点是第一次投票,直接拒绝该投票请求,同时会将自己的firstVote字段置为该Candidate的节点索引。
1~4步骤中都没有拒绝投票请求
同意该投票请求。
在Leader成为网络孤岛时,Leader可以发出心跳、Follower可以收到心跳但是Leader收不到心跳回应,这种情况下Leader此时已经出现网络异常,但是由于一直可以向外发送心跳包会导致Follower无法切换状态进行选取,系统陷入停滞。为了避免第二种情况发生,模块中设置了心跳超时机制,Leader每次收到心跳回应时会进行相应记录,一旦一段时间后记录没有更新则Leader放弃Leader身份并转换为Follower节点。
3.2 区块复制¶
Raft协议强依赖Leader节点的可用性来确保集群数据的一致性,因为数据只能从Leader节点向Follower节点转移。当Raft Sealer向集群Leader提交区块数据后,Leader将该数据置为未提交(uncommitted)状态,接着Leader 节点会通过在Heartbeat中附加数据的形式并发向所有Follower节点复制数据并等待接收响应,在确保网络中超过半数节点已接收到数据后,再将区块数据写入底层存储中,此时区块数据状态已经进入已提交(committed)状态。此后Leader节点再通过Sync模块向其他Follower节点广播该区块数据,区块复制及提交的流程图如下图所示:
其中RaftSealer验证是否当前是否能打包交易的验证条件包括:(1) 是否为Leader;(2) 是否存在尚未完成同步的peer; (3) uncommitBlock字段是否为空,只有三个条件均符合才允许打包。
虚拟机与合约¶
交易的执行是区块链节点上的一个重要的功能。交易的执行,是把交易中的智能合约二进制代码取出来,用执行器(Executor)执行。共识模块(Consensus)把交易从交易池中取出,打包成区块,并调用执行器去执行区块中的交易。在交易的执行过程中,会对区块链状态的状态(State)进行修改,形成新区块的状态储存下来(Storage)。执行器在这个过程中,类似于一个黑盒,输入是智能合约代码,输出是状态的改变。
随着技术的发展,人们开始关注执行器的性能和易用性。一方面,人们希望智能合约在区块链上能有更快的执行速度,满足大规模交易的需求。另一方面,人们希望能用更熟悉更好用的语言进行开发。进而出现了一些替代传统的执行器(EVM)的方案,如JIT、WASM甚至JVM。然而,传统的EVM是耦合在节点代码中的。首先要做的,是将执行器的接口抽象出来,兼容各种虚拟机的实现。因此,EVMC被设计出来。
EVMC (Ethereum Client-VM Connector API),是以太坊抽象出来的执行器的接口,旨在能够对接各种类型的执行器。FISCO BCOS目前采用了以太坊的智能合约语言Solidity,因此也沿用了以太坊对执行器接口的抽象。

在节点上,共识模块会调用EVMC,将打包好的交易交由执行器执行。执行器执行时,对状态进行的读写,会通过EVMC的回调反过来操作节点上的状态数据。
经过EVMC一层的抽象,FISCO BCOS能够对接今后出现的更高效、易用性更强的执行器。
目前,FISCO BCOS采用的是传统的EVM根据EVMC抽象出来的执行器—-Interpreter。因此能够支持基于Solidity语言的智能合约。目前其他类型的执行器发展尚未成熟,后续将持续跟进。
EVM 以太坊虚拟机¶
以太坊虚拟机,可理解为一个执行器,输入是指令和当前的状态数据,输出是对状态的改变。
EVMC – Ethereum Client-VM Connector API¶
新版本的以太坊将EVM从节点代码中剥离出来,形成一个独立的模块。EVM与节点的交互,抽象出EVMC接口标准。通过EVMC,节点可以对接多种虚拟机,而不仅限于传统的基于solidity的虚拟机。
传统的solidity虚拟机,在以太坊中称为interpreter,下文主要解释interpreter的实现。
EVMC 接口¶
EVMC主要定义了两种调用的接口:
- Instance接口:节点调用EVM的接口
- Callback接口:EVM回调节点的接口
EVM本身不保存状态数据,节点通过instance接口操作EVM,EVM反过来,调Callback接口,对节点的状态进行操作。
Instance 接口
定义了节点对虚拟机的操作,包括创建,销毁,设置等。
此处给出相关代码及注释(evmc.h)
/**
* The EVM instance.
*
* Defines the base struct of the EVM implementation.
*/
struct evmc_instance
{
/**
* EVMC ABI version implemented by the EVM instance.
*
* Used to detect ABI incompatibilities. The EVMC ABI version
* represented by this file is in ::EVMC_ABI_VERSION.
*/
const int abi_version;
/**
* The name of the EVMC VM implementation.
*
* It MUST be a NULL-terminated not empty string.
*/
const char* name;
/**
* The version of the EVMC VM implementation, e.g. "1.2.3b4".
*
* It MUST be a NULL-terminated not empty string.
*/
const char* version;
/** Pointer to function destroying the EVM instance. */
evmc_destroy_fn destroy;
/** Pointer to function executing a code by the EVM instance. */
evmc_execute_fn execute;
/**
* Optional pointer to function setting the EVM instruction tracer.
*
* If the EVM does not support this feature the pointer can be NULL.
*/
evmc_set_tracer_fn set_tracer;
/**
* Optional pointer to function modifying VM's options.
*
* If the VM does not support this feature the pointer can be NULL.
*/
evmc_set_option_fn set_option;
};
Callback接口
定义了EVM对节点的操作,主要是对state读写、区块信息的读写等。
此处给出相关代码及注释(evmc.h)
struct evmc_context_fn_table
{
/** Check account existence callback function. */
evmc_account_exists_fn account_exists;
/** Get storage callback function. */
evmc_get_storage_fn get_storage;
/** Set storage callback function. */
evmc_set_storage_fn set_storage;
/** Get balance callback function. */
evmc_get_balance_fn get_balance;
/** Get code size callback function. */
evmc_get_code_size_fn get_code_size;
/** Get code hash callback function. */
evmc_get_code_hash_fn get_code_hash;
/** Copy code callback function. */
evmc_copy_code_fn copy_code;
/** Selfdestruct callback function. */
evmc_selfdestruct_fn selfdestruct;
/** Call callback function. */
evmc_call_fn call;
/** Get transaction context callback function. */
evmc_get_tx_context_fn get_tx_context;
/** Get block hash callback function. */
evmc_get_block_hash_fn get_block_hash;
/** Emit log callback function. */
evmc_emit_log_fn emit_log;
};
EVM 执行¶
EVM 指令¶
solidity是合约的执行语言,solidity被solc编译后,变成类似于汇编的EVM指令。Interpreter定义了一套完整的指令集。solidity被编译后,生成二进制文件,二进制文件就是EVM指令的集合,交易以二进制的形式发往节点,节点收到后,通过EVMC调用EVM执行这些指令。在EVM中,用代码实现了这些指令的逻辑。
enum class Instruction : uint8_t
{
STOP = 0x00, ///< halts execution
ADD, ///< addition operation
MUL, ///< mulitplication operation
SUB, ///< subtraction operation
DIV, ///< integer division operation
SDIV, ///< signed integer division operation
MOD, ///< modulo remainder operation
SMOD, ///< signed modulo remainder operation
ADDMOD, ///< unsigned modular addition
MULMOD, ///< unsigned modular multiplication
EXP, ///< exponential operation
SIGNEXTEND, ///< extend length of signed integer
LT = 0x10, ///< less-than comparision
GT, ///< greater-than comparision
SLT, ///< signed less-than comparision
SGT, ///< signed greater-than comparision
EQ, ///< equality comparision
ISZERO, ///< simple not operator
AND, ///< bitwise AND operation
OR, ///< bitwise OR operation
XOR, ///< bitwise XOR operation
NOT, ///< bitwise NOT operation
BYTE, ///< retrieve single byte from word
SHL, ///< logical shift left operation
SHR, ///< logical shift right operation
SAR, ///< arithmetic shift right operation
SHA3 = 0x20, ///< compute SHA3-256 hash
ADDRESS = 0x30, ///< get address of currently executing account
BALANCE, ///< get balance of the given account
ORIGIN, ///< get execution origination address
CALLER, ///< get caller address
CALLVALUE, ///< get deposited value by the instruction/transaction responsible for this
///< execution
CALLDATALOAD, ///< get input data of current environment
CALLDATASIZE, ///< get size of input data in current environment
CALLDATACOPY, ///< copy input data in current environment to memory
CODESIZE, ///< get size of code running in current environment
CODECOPY, ///< copy code running in current environment to memory
GASPRICE, ///< get price of gas in current environment
EXTCODESIZE, ///< get external code size (from another contract)
EXTCODECOPY, ///< copy external code (from another contract)
RETURNDATASIZE = 0x3d, ///< size of data returned from previous call
RETURNDATACOPY = 0x3e, ///< copy data returned from previous call to memory
EXTCODEHASH = 0x3f, ///< get external code hash
BLOCKHASH = 0x40, ///< get hash of most recent complete block
COINBASE, ///< get the block's coinbase address
TIMESTAMP, ///< get the block's timestamp
NUMBER, ///< get the block's number
DIFFICULTY, ///< get the block's difficulty
GASLIMIT, ///< get the block's gas limit
POP = 0x50, ///< remove item from stack
MLOAD, ///< load word from memory
MSTORE, ///< save word to memory
MSTORE8, ///< save byte to memory
SLOAD, ///< load word from storage
SSTORE, ///< save word to storage
JUMP, ///< alter the program counter to a jumpdest
JUMPI, ///< conditionally alter the program counter
PC, ///< get the program counter
MSIZE, ///< get the size of active memory
GAS, ///< get the amount of available gas
JUMPDEST, ///< set a potential jump destination
PUSH1 = 0x60, ///< place 1 byte item on stack
PUSH2, ///< place 2 byte item on stack
PUSH3, ///< place 3 byte item on stack
PUSH4, ///< place 4 byte item on stack
PUSH5, ///< place 5 byte item on stack
PUSH6, ///< place 6 byte item on stack
PUSH7, ///< place 7 byte item on stack
PUSH8, ///< place 8 byte item on stack
PUSH9, ///< place 9 byte item on stack
PUSH10, ///< place 10 byte item on stack
PUSH11, ///< place 11 byte item on stack
PUSH12, ///< place 12 byte item on stack
PUSH13, ///< place 13 byte item on stack
PUSH14, ///< place 14 byte item on stack
PUSH15, ///< place 15 byte item on stack
PUSH16, ///< place 16 byte item on stack
PUSH17, ///< place 17 byte item on stack
PUSH18, ///< place 18 byte item on stack
PUSH19, ///< place 19 byte item on stack
PUSH20, ///< place 20 byte item on stack
PUSH21, ///< place 21 byte item on stack
PUSH22, ///< place 22 byte item on stack
PUSH23, ///< place 23 byte item on stack
PUSH24, ///< place 24 byte item on stack
PUSH25, ///< place 25 byte item on stack
PUSH26, ///< place 26 byte item on stack
PUSH27, ///< place 27 byte item on stack
PUSH28, ///< place 28 byte item on stack
PUSH29, ///< place 29 byte item on stack
PUSH30, ///< place 30 byte item on stack
PUSH31, ///< place 31 byte item on stack
PUSH32, ///< place 32 byte item on stack
DUP1 = 0x80, ///< copies the highest item in the stack to the top of the stack
DUP2, ///< copies the second highest item in the stack to the top of the stack
DUP3, ///< copies the third highest item in the stack to the top of the stack
DUP4, ///< copies the 4th highest item in the stack to the top of the stack
DUP5, ///< copies the 5th highest item in the stack to the top of the stack
DUP6, ///< copies the 6th highest item in the stack to the top of the stack
DUP7, ///< copies the 7th highest item in the stack to the top of the stack
DUP8, ///< copies the 8th highest item in the stack to the top of the stack
DUP9, ///< copies the 9th highest item in the stack to the top of the stack
DUP10, ///< copies the 10th highest item in the stack to the top of the stack
DUP11, ///< copies the 11th highest item in the stack to the top of the stack
DUP12, ///< copies the 12th highest item in the stack to the top of the stack
DUP13, ///< copies the 13th highest item in the stack to the top of the stack
DUP14, ///< copies the 14th highest item in the stack to the top of the stack
DUP15, ///< copies the 15th highest item in the stack to the top of the stack
DUP16, ///< copies the 16th highest item in the stack to the top of the stack
SWAP1 = 0x90, ///< swaps the highest and second highest value on the stack
SWAP2, ///< swaps the highest and third highest value on the stack
SWAP3, ///< swaps the highest and 4th highest value on the stack
SWAP4, ///< swaps the highest and 5th highest value on the stack
SWAP5, ///< swaps the highest and 6th highest value on the stack
SWAP6, ///< swaps the highest and 7th highest value on the stack
SWAP7, ///< swaps the highest and 8th highest value on the stack
SWAP8, ///< swaps the highest and 9th highest value on the stack
SWAP9, ///< swaps the highest and 10th highest value on the stack
SWAP10, ///< swaps the highest and 11th highest value on the stack
SWAP11, ///< swaps the highest and 12th highest value on the stack
SWAP12, ///< swaps the highest and 13th highest value on the stack
SWAP13, ///< swaps the highest and 14th highest value on the stack
SWAP14, ///< swaps the highest and 15th highest value on the stack
SWAP15, ///< swaps the highest and 16th highest value on the stack
SWAP16, ///< swaps the highest and 17th highest value on the stack
LOG0 = 0xa0, ///< Makes a log entry; no topics.
LOG1, ///< Makes a log entry; 1 topic.
LOG2, ///< Makes a log entry; 2 topics.
LOG3, ///< Makes a log entry; 3 topics.
LOG4, ///< Makes a log entry; 4 topics.
// these are generated by the interpreter - should never be in user code
PUSHC = 0xac, ///< push value from constant pool
JUMPC, ///< alter the program counter - pre-verified
JUMPCI, ///< conditionally alter the program counter - pre-verified
JUMPTO = 0xb0, ///< alter the program counter to a jumpdest
JUMPIF, ///< conditionally alter the program counter
JUMPSUB, ///< alter the program counter to a beginsub
JUMPV, ///< alter the program counter to a jumpdest
JUMPSUBV, ///< alter the program counter to a beginsub
BEGINSUB, ///< set a potential jumpsub destination
BEGINDATA, ///< begine the data section
RETURNSUB, ///< return to subroutine jumped from
PUTLOCAL, ///< pop top of stack to local variable
GETLOCAL, ///< push local variable to top of stack
XADD = 0xc1, ///< addition operation
XMUL, ///< mulitplication operation
XSUB, ///< subtraction operation
XDIV, ///< integer division operation
XSDIV, ///< signed integer division operation
XMOD, ///< modulo remainder operation
XSMOD, ///< signed modulo remainder operation
XLT = 0xd0, ///< less-than comparision
XGT, ///< greater-than comparision
XSLT, ///< signed less-than comparision
XSGT, ///< signed greater-than comparision
XEQ, ///< equality comparision
XISZERO, ///< simple not operator
XAND, ///< bitwise AND operation
XOOR, ///< bitwise OR operation
XXOR, ///< bitwise XOR operation
XNOT, ///< bitwise NOT opertation
XSHL = 0xdb, ///< shift left opertation
XSHR, ///< shift right opertation
XSAR, ///< shift arithmetic right opertation
XROL, ///< rotate left opertation
XROR, ///< rotate right opertation
XPUSH = 0xe0, ///< push vector to stack
XMLOAD, ///< load vector from memory
XMSTORE, ///< save vector to memory
XSLOAD = 0xe4, ///< load vector from storage
XSSTORE, ///< save vector to storage
XVTOWIDE, ///< convert vector to wide integer
XWIDETOV, ///< convert wide integer to vector
XGET, ///< get data from vector
XPUT, ///< put data in vector
XSWIZZLE, ///< permute data in vector
XSHUFFLE, ///< permute data in two vectors
CREATE = 0xf0, ///< create a new account with associated code
CALL, ///< message-call into an account
CALLCODE, ///< message-call with another account's code only
RETURN, ///< halt execution returning output data
DELEGATECALL, ///< like CALLCODE but keeps caller's value and sender
CREATE2 = 0xf5, ///< create a new account with associated code. sha3((sender + salt + code)
STATICCALL = 0xfa, ///< like CALL except state changing operation are not permitted (will
///< throw)
REVERT = 0xfd, ///< stop execution and revert state changes, without consuming all provided gas
INVALID = 0xfe, ///< dedicated invalid instruction
SUICIDE = 0xff ///< halt execution and register account for later deletion
};
Solidity是基于堆栈的语言,EVM在执行二进制时,也是以堆栈的方式进行调用。
算术指令举例
一条ADD指令,在EVM中的代码实现如下。SP是堆栈的指针,从栈顶第一和第二个位置(SP[0]
、SP[1]
)拿出数据,进行加和后,写入结果堆栈SPP的顶端SPP[0]
。
CASE(ADD)
{
ON_OP();
updateIOGas();
// pops two items and pushes their sum mod 2^256.
m_SPP[0] = m_SP[0] + m_SP[1];
}
跳转指令举例
JUMP指令,实现了二进制代码间的跳转。首先从堆栈顶端SP[0]
取出待跳转的地址,验证一下是否越界,放到程序计数器PC中,下一个指令,将从PC指向的位置开始执行。
CASE(JUMP)
{
ON_OP();
updateIOGas();
m_PC = verifyJumpDest(m_SP[0]);
}
状态读指令举例
SLOAD可以查询状态数据。大致过程是,从堆栈顶端SP[0]
取出要访问的key,把key作为参数,然后调evmc的callback函数get_storage()
,查询相应的key对应的value。之后将读到的value写到结果堆栈SPP的顶端SPP[0]
。
CASE(SLOAD)
{
m_runGas = m_rev >= EVMC_TANGERINE_WHISTLE ? 200 : 50;
ON_OP();
updateIOGas();
evmc_uint256be key = toEvmC(m_SP[0]);
evmc_uint256be value;
m_context->fn_table->get_storage(&value, m_context, &m_message->destination, &key);
m_SPP[0] = fromEvmC(value);
}
状态写指令举例
SSTORE指令可以将数据写到节点的状态中,大致过程是,从栈顶第一和第二个位置(SP[0]
、SP[1]
)拿出key和value,把key和value作为参数,调用evmc的callback函数set_storage()
,写入节点的状态。
CASE(SSTORE)
{
ON_OP();
if (m_message->flags & EVMC_STATIC)
throwDisallowedStateChange();
static_assert(
VMSchedule::sstoreResetGas <= VMSchedule::sstoreSetGas, "Wrong SSTORE gas costs");
m_runGas = VMSchedule::sstoreResetGas; // Charge the modification cost up front.
updateIOGas();
evmc_uint256be key = toEvmC(m_SP[0]);
evmc_uint256be value = toEvmC(m_SP[1]);
auto status =
m_context->fn_table->set_storage(m_context, &m_message->destination, &key, &value);
if (status == EVMC_STORAGE_ADDED)
{
// Charge additional amount for added storage item.
m_runGas = VMSchedule::sstoreSetGas - VMSchedule::sstoreResetGas;
updateIOGas();
}
}
合约调用指令举例
CALL指令能够根据地址调用另外一个合约。首先,EVM判断是CALL指令,调用caseCall()
,在caseCall()中,用
caseCallSetup()从堆栈中拿出数据,封装成msg,作为参数,调用evmc的callback函数call。Eth在被回调
call()后,启动一个新的EVM,处理调用,之后将新的EVM的执行结果,通过
call()```的参数返回给当前的EVM,当前的EVM将结果写入结果堆栈SSP中,调用结束。合约创建的逻辑与此逻辑类似。
CASE(CALL)
CASE(CALLCODE)
{
ON_OP();
if (m_OP == Instruction::DELEGATECALL && m_rev < EVMC_HOMESTEAD)
throwBadInstruction();
if (m_OP == Instruction::STATICCALL && m_rev < EVMC_BYZANTIUM)
throwBadInstruction();
if (m_OP == Instruction::CALL && m_message->flags & EVMC_STATIC && m_SP[2] != 0)
throwDisallowedStateChange();
m_bounce = &VM::caseCall;
}
BREAK
void VM::caseCall()
{
m_bounce = &VM::interpretCases;
evmc_message msg = {};
// Clear the return data buffer. This will not free the memory.
m_returnData.clear();
bytesRef output;
if (caseCallSetup(msg, output))
{
evmc_result result;
m_context->fn_table->call(&result, m_context, &msg);
m_returnData.assign(result.output_data, result.output_data + result.output_size);
bytesConstRef{&m_returnData}.copyTo(output);
m_SPP[0] = result.status_code == EVMC_SUCCESS ? 1 : 0;
m_io_gas += result.gas_left;
if (result.release)
result.release(&result);
}
else
{
m_SPP[0] = 0;
m_io_gas += msg.gas;
}
++m_PC;
}
总结¶
EVM是一个状态执行的机器,输入是solidity编译后的二进制指令和节点的状态数据,输出是节点状态的改变。以太坊通过EVMC实现了多种虚拟机的兼容。但截至目前,并未出现除开interpreter之外的,真正生产可用的虚拟机。也许要做到同一份代码在不同的虚拟机上跑出相同的结果,是一件很难的事情。BCOS将持续跟进此部分的发展。
Precompiled¶
Precompiled合约提供一种使用C++编写合约的方法,合约逻辑与数据分离,相比于solidity合约具有更好的性能,可以通过修改底层代码实现合约升级。
Precompiled合约与Solidity合约对比¶
表名 | Precompiled合约 | Solidity合约 |
---|---|---|
地址 | 固定地址,代码中定义 | 部署时确定 |
合约代码 | 数据存储在表中,与合约分离,可升级合约逻辑 | 合约变量和数据存储在MPT树中 |
执行 | C++底层执行,性能更高,可实现并行 | EVM虚拟机,串行执行 |
模块架构¶
Precompiled的架构如下图所示:
- 区块验证器在执行交易的时候会根据被调用合约的地址来判断类型。地址1-4表示以太坊预编译合约,地址0x1000-0x10000是C++预编译合约,其他地址是EVM合约。
关键流程¶
- 执行Precompiled合约时首先需要根据合约地址获取到预编译合约的对象。
- 每个预编译合约对象都会实现
call
接口,预编译合约的具体逻辑在该接口中实现。 call
根据交易的abi编码,获取到Function Selector
和参数,然后执行对应的逻辑。
接口定义¶
每个Precompiled合约都必须实现自己的call
接口,接口接受三个参数,分别是ExecutiveContext
执行上下文、bytesConstRef
参数的abi编码和外部账户地址,其中外部账户地址用于判断是否具有写权限。Precompiled
源码。
接口名 | 参数说明 | 接口说明 |
---|---|---|
virtual bytes call(std::shared_ptr<ExecutiveContext> context, bytesConstRef param, Address const& origin = Address()) |
context 为区块执行上下文,param 为abi编码的参数,origin 为调用的外部账户地址 |
具体合约接口的实现 |
virtual uint32_t getParamFunc(bytesConstRef param) |
param 为abi编码的参数 |
获取调用的函数的Function Select (函数名的sha3的前四个大端字节) |
virtual uint32_t getFuncSelector(std::string const& _functionName) |
_functionName 为函数名 |
根据函数名计算Function Select |
virtual bytesConstRef getParamData(bytesConstRef param) |
param 为abi编码的参数 |
获取调用函数的具体参数的abi编码 |
存储模块¶
FISCO BCOS继承以太坊存储的同时,引入了高扩展性、高吞吐量、高可用、高性能的分布式存储。存储模块主要包括两部分:
世界状态: 可进一步划分成 MPTState 和 StorageState
- MPTState: 使用MPT树存储账户的状态,与以太坊一致
- StorageState: 使用分布式存储的表结构存储账户状态,不存历史信息,去掉了对MPT树的依赖,性能更高
分布式存储(Advanced Mass Database,AMDB): 通过抽象表结构,实现了SQL和NOSQL的统一,通过实现对应的存储驱动,可以支持各类数据库,目前已经实现支持LevelDB。

MPT State¶
MPT State是以太坊上级经典的数据存储方式。通过MPT树的方式,将所有合约的数据组织起来,实现了对数据的查找和追溯。
MPT树¶
MPT(Merkle Paricia Trie),是一种用hash索引数据的前缀树。
从宏观上来说,在MPT树是一棵前缀树,用key查询value。通过key去查询value,就是用key去在MPT树上进行索引,在经过多个中间节点后,最终到达存储数据的叶子节点。
从细节上来说,MPT树,是一棵Merkle树,每个树上节点的索引,都是这个节点的hash值。在用key查找value的时候,是根据key在某节点内部,获取下一个需要跳转的节点的hash值,拿到下一个节点的hash值,才能从底层的数据库中取出下一个节点的数据,之后,再用key,去下一个节点中查询下下个节点的hash值,直至到达value所在的叶子节点。
当MPT树上某个叶子节点的数据更新后,此叶子节点的hash也会更新,随之而来的,是这个叶子节点回溯到根节点的所有中间节点的hash都会更新。最终,MPT根节点的hash也会更新。当要索引这个新的数据时,用MPT新的根节点hash,从底层数据库查出新的根节点,再往后一层层遍历,最终找到新的数据。而如果要查询历史数据,则可用老的树根hash,从底层数据库取出老的根节点,再往下遍历,就可查询到历史的数据。
MPT树的实现图(图片来自以太坊黄皮书)
状态 State¶
在以太坊上,数据是以account为单位存储的,每个account内,保存着这个合约(用户)的代码、参数、nonce等数据。account的数据,通过account的地址(address)进行索引。以太坊上用MPT将这些address作为查询的key,实现了对account的查询。
随着account数据的改变,account的hash也进行改变。于此同时,MPT的根的hash也会改变。不同的时候,account的数据不同,对应的MPT的根就不同。此处,以太坊把这层含义进行了具体化,提出了“状态”的概念。把MPT根的hash,叫state root。不同的state root,对应着不同的“状态”,对应查询到不同的MPT根节点,再用account的address从不同的MPT根节点查询到此状态下的account数据。不同的state,拿到的MPT根节点不同,查询的account也许会有不同。
state root是区块中的一个字段,每个区块对应着不同的“状态”。区块中的交易会对account进行操作,进而改变account中的数据。不同的区块下,account的数据有所不同,即此区块的状态有所不同,具体的,是state root不同。从某个区块中取出这个区块的state root,查询到MPT的根节点,就能索引到这个区块当时account的数据历史。
(图片来自以太坊白皮书)
Trade Off¶
MPT State的引入,是为了实现对数据的追溯。根据不同区块下的state root,就能查询到当时区块下account的历史信息。而MPT State的引入,带来了大量hash的计算,同时也打散了底层数据的存储的连续性。在性能方面,MPT State存在着天然的劣势。可以说,MPT State是极致的最求可追溯性,而大大的忽略了性能。
在FISCO BCOS的业务场景中,性能与可追溯性相比,更为重要。FISCO BCOS对底层的存储进行了重新的设计,实现了Storage State。Storage State牺牲了部分的可追溯性,但带来了性能上的提升。
AMDB¶
分布式存储(Advanced Mass Database,AMDB)通过对表结构的设计,既可以对应到关系型数据库的表,又可以拆分使用KV数据库存储。通过实现对应于不同数据库的存储驱动,AMDB理论上可以支持所有关系型和KV的数据库。
- CRUD数据、区块数据和合约代码数据存储默认情况下都保存在AMDB,无需配置,合约局部变量存储可根据需要配置为MPTState或StorageState,无论配置哪种State,合约代码都不需要变动。
- 当使用MPTState时,合约局部变量保存在MPT树中。当使用StorageState时,合约局部变量保存在AMDB表中。
- 尽管MPTState和AMDB最终数据都会写向LevelDB,但二者使用不同的LevelDB实例,没有事务性,因此当配置成使用MPTState时,提交数据时异常可能导致两个LevelDB数据不一致。
名词解释¶
Table¶
存储表中的所有数据。Table中存储AMDB主key到对应Entries的映射,可以基于AMDB主key进行增删改查,支持条件筛选。
Entries¶
Entries中存放主Key相同的Entry,数组。AMDB的主Key与Mysql中的主key不同,AMDB主key用于标示Entry属于哪个key,相同key的Entry会存放在同一个Entries中。
Entry¶
对应于表中的一行,每行以列名作为key,对应的值作为value,构成KV结构。每个Entry拥有自己的AMDB主key,不同Entry允许拥有相同的AMDB主key。
Condition¶
Table中的删改查接口支持传入条件,这三种接口会返回根据条件筛选后的结果。如果条件为空,则不做任何筛选。
举例¶
下面以某个资产统计表为例,解释上述名词。
姓名* | 资产名 | 价值/w |
---|---|---|
老王 | 住房 | 500 |
老王 | 车 | 30 |
小李 | 住房 | 600 |
小张 | 车 | 15 |
解释如下:
- 上面可以作为一个Table,姓名是AMDB主key。
- 表中的每一行为一个Entry。一共有4个Entry,每个Entry以Map存储数据,其中列名为key,对应值为value存。
- Table中以姓名为主key,存有3个Entries对象。第1个Entries中存有老王的2条记录,第2个Entries中存有小李的1条记录,第3个Entries中存有小张的一条记录。
- 调用Table类的查询接口时,可以设置
资产价值 > 40
,会查询出老王和小李的两条记录。
AMDB表分类¶
表中的所有entry
,都会有_status_
,_num_
,_hash_
内置字段。
系统表¶
系统表默认存在,由存储驱动保证系统表的创建。
表名 | keyField | valueField | 存储数据说明 | AMDB主key |
---|---|---|---|---|
_sys_tables_ |
table_name | key_field,value_field | 存储所有表的结构,以表名为主键 | 所有表的表名 |
_sys_consensus_ |
name | type,node_id,enable_num | 存储共识节点和观察节点的列表 | node |
_sys_table_access_ |
table_name | address,enable_num | 存储每个表的具有写权限的外部账户地址 | 表的表名 |
_sys_cns_ |
name | version,address,abi | 存储CNS映射关系 | 合约名 |
_sys_config_ |
key | value,enable_num | 存储需要共识的群组配置项 | 配置项 |
_sys_current_state_ |
key | value | 存储最新的状态 | current_number/total_transaction_count |
_sys_tx_hash_2_block_ |
hash | value,index | 存储交易hash到区块号的映射 | 交易hash的16进制 |
_sys_number_2_hash_ |
hash | value | 存储区块号到区块头hash的16进制表示的映射 | 区块高 |
_sys_hash_2_block_ |
key | value | 存储hash到序列化的区块数据 | 区块头hash的16进制 |
_sys_block_2_nonces_ |
number | value | 存储区块中交易的nonces | 区块高 |
用户表¶
用户CRUD合约所创建的表,以_user_<TableName>
为表名,底层自动添加_user_
前缀。
StorageState账户表¶
_contract_data_
+Address
+_
作为表名。表中存储外部账户相关信息。
StorageState¶
StorageState是一种使用AMDB实现的存储账户状态的方式。相比于MPTState主要有以下区别:
StorageState | MPTState | |
---|---|---|
账户数据组织方式 | AMDB表 | MPT树 |
历史状态 | 不支持,不维护历史状态 | 支持 |
MPTState每个账户使用MPT树存储其数据,当历史数据逐渐增多时,会因为存储方式和磁盘IO导致性能问题。StorageState每个账户对应一个Table存储其相关数据,包括账户的nonce
,code
,balance
等内容,而AMDB可以通过实现对应的存储驱动支持不同的数据库以提高性能,我们使用LevelDB测试发现,StorageState性能大约是MPTState的两倍。
安全控制¶
为了保障节点间通信安全性,以及对节点数据访问的安全性,FISCO BCOS引入了节点准入机制、CA黑名单和权限控制三种机制,在网络和存储层面上做了严格的安全控制。
网络层面安全控制
- 节点使用 SSL连接 ,保障了通信数据的机密性
- 引入 网络准入机制 ,可将指定群组的作恶节点从共识节点列表或群组中删除,保障了系统安全性
- 通过 群组白名单机制 ,保证每个群组仅可接收相应群组的消息,保证群组间通信数据的隔离性
- 引入 CA黑名单机制 ,可及时与作恶节点断开网络连接
- 提出 分布式存储权限控制 机制,灵活、细粒度地控制外部账户部署合约和创建、插入、删除和更新用户表的权限。
存储层面安全控制
基于分布式存储,提出分布式存储权限控制的机制,以灵活、细粒度的方式进行有效的权限控制,设计并实现了权限控制机制限制外部账户(tx.origin)对存储的访问,权限控制范围包括合约部署、表的创建、表的写操作。
节点准入管理介绍¶
本文档对节点准入管理进行介绍性说明,实践方法参见《节点准入管理操作文档》。
概述¶
单链多账本¶
区块链技术是一种去中心化、公开透明的分布式数据存储技术,能够降低信任成本,实现安全可靠的数据交互。然而区块链的交易数据面临着隐私泄露威胁:
- 对于公有链,一节点可任意加入网络,从全局账本中获得所有数据;
- 对于联盟链,虽有网络准入机制,但节点加入区块链后即可获取全局账本的数据。
作为联盟链的FISCO BCOS,对链上隐私这一问题,提出了单链多账本的解决方案。FISCO BCOS通过引入群组概念,使联盟链从原有一链一账本的存储/执行机制扩展为一链多账本的存储/执行机制,基于群组维度实现同一条链上的数据隔离和保密。
如上图所示,节点ABC加入蓝色群组,并共同维护蓝色账本; 节点B和C加入橙色群组并维护橙色账本; 节点A和B加入绿色群组并维护绿色账本。三个群组间共享公共的网络服务,但各群组有各自私有的账本存储及交易执行环境。客户端将交易发到节点所属的某个群组上,该群组内部对交易及数据进行共识并存储,其他群组对该交易无感知不可见。
节点准入机制¶
基于群组概念的引入,节点准入管理可分为网络准入机制和群组准入机制。准入机制的规则记录在配置中,节点启动后将读取配置信息实现网络及群组的准入判断。
名词解释¶
节点类型¶
本文档所讨论的节点为已完成网络准入可进行P2P通信的节点。网络准入过程涉及P2P节点连接列表添加和证书验证。
- 群组节点:完成网络准入并加入群组的节点。群组节点只能是共识节点和观察节点两者之一。其中共识节点参与共识出块和交易/区块同步,观察节点只参与区块同步。群组节点准入过程涉及动态增删节点的交易发送。
- 游离节点:完成网络准入但没有加入群组的节点。游离节点尚未通过群组准入,不参与共识和同步。
节点关系如下:
配置类型¶
划分维度 | 配置类型 | |
影响范围 | 网络配置 | 全局性质的配置,节点的配置影响该节点所在的整个网络,节点对整个网络使用同一份配置, 文件名为config.* |
群组配置 | 节点的配置影响该节点所在的单个群组,每个群组各有一份配置, 文件名为group.X.*,其中X为群组号 |
|
是否可改 | 固定配置 | 只使用首次配置内容,后续对配置的修改无效, 文件后缀为.genesis |
可改配置 | 配置后续可改动,节点重启生效, 文件后缀为.ini |
|
存放位置 | 本地存储 | 配置存放在本地文件,用户可直接修改, 用户修改自身文件能重启生效的配置项 |
链上存储 | 配置存放在区块链上,对其修改需群组共识,目前没有需全网共识的内容, 需新链重置或通过交易修改生效的配置项 |
节点准入配置项¶
涉及节点转入管理相关的配置项有:P2P节点连接列表,节点证书,CA黑名单,群组节点初始列表和群组节点系统表。
P2P节点连接列表 | 记录本节点期望与哪些节点建立网络通信 | 网络配置 | 可改配置 | 本地存储 |
节点证书 | 证明自己是由可信第三方许可的节点 | 网络配置 | 可改配置 | 本地存储 |
CA黑名单 | 记录本节点禁止与哪些节点建立网络通信 | 网络配置 | 可改配置 | 本地存储 |
群组节点初始列表 | 记录创世块阶段参与共识/同步的节点列表 | 群组配置 | 固定配置 | 本地存储 |
群组节点系统表 | 记录当前参与一群组共识/同步的节点列表 | 群组配置 | 可改配置 | 链上存储 |
核心流程¶
首次初始化流程¶
节点在首次启动时,对其所属的各个群组,以群组为单位将固定配置文件的内容写入第0块并直接提交上链。初始化的具体逻辑为:
这一阶段需写入的与节点准入管理相关的配置内容有:群组节点初始列表->群组节点系统表。
说明:
- 同一账本的所有节点的第0块需一致,即固定配置文件均一致;
- 节点后续的每次启动均检查第0块信息是否与固定配置文件一致。如果固定配置文件被修改,节点再次启动将输出告警信息,但不会影响群组正常运作。
基于CA黑名单的节点建连流程¶
SSL认证用于确定节点之间是否许可加入某条链。一条链上的节点均信任可信的第三方(节点证书的颁发者)。
FISCO BCOS要求实现SSL双向认证。节点在handshake过程中,从对方节点提供的证书中获取对方节点的nodeID,检查该nodeID是否在自身的CA黑名单。如存在,关闭该connect,如不在,建立session。
CA黑名单机制也支持SSL单向认证的场景,作用时机是:节点在session建立后,可从session中获取对方节点的nodeID进行判断,如果nodeID在自身的CA黑名单中,将已建立的session断连。
接口及配置描述¶
节点配置文件层级¶
配置文件的组织规则为:各群组的配置独立、固定配置和可改配置相独立。目前使用的文件有网络可改配置文件config.ini
、群组固定配置文件group.N.genesis
和群组可改配置文件group.N.ini
,其中N
为节点所在的群组号。对于网络/群组可改配置文件,如果文件中没有显式定义一配置项的值,程序将使用该配置项的默认值。
配置文件示例¶
对于网络可改配置文件config.ini
,节点准入管理涉及P2P节点连接列表[p2p]
、节点证书[secure]
、CA黑名单[crl]
。[crl]
可缺少。配置项举例如下:
[p2p]
;p2p listen ip
listen_ip=0.0.0.0
;p2p listen port
listen_port=30300
;nodes to connect
node.0=127.0.0.1:30300
node.1=127.0.0.1:30301
node.2=127.0.0.1:30302
node.3=127.0.0.1:30303
;certificate rejected list
[crl]
;crl.0 should be nodeid, nodeid's length is 128
;crl.0=
;certificate configuration
[secure]
;directory the certificates located in
data_path=conf/
;the node private key file
key=node.key
;the node certificate file
cert=node.crt
;the ca certificate file
ca_cert=ca.crt
对于群组固定配置文件group.N.genesis
,节点准入管理涉及群组节点初始列表[consensus]。配置项举例如下:
;consensus configuration
[consensus]
;consensus algorithm type, now support PBFT(consensus_type=pbft) and Raft(consensus_type=raft)
consensus_type=pbft
;the max number of transactions of a block
max_trans_num=1000
;the node id of leaders
node.0=79d3d4d78a747b1b9e59a3eb248281ee286d49614e3ca5b2ce3697be2da72cfa82dcd314c0f04e1f590da8db0b97de466bd08e27eaa13f85df9b60e54d6a1ec8
node.1=da527a4b2aeae1d354102c6c3ffdfb54922a092cc9acbdd555858ef89032d7be1be499b6cf9a703e546462529ed9ea26f5dd847110ff3887137541bc651f1c32
node.2=160ba08898e1e25b31e24c2c4e3c75eed996ec56bda96043aa8f27723889ab774b60e969d9bd25d70ea8bb8779b7070521d9bd775dc7636f4b2b800d2fc8c7dd
node.3=a968f1e148e4b51926c5354e424acf932d61f67419cf7c5c00c7cb926057c323bee839d27fe9ad6c75386df52ae2b30b2e7ba152b0023979d25dee25b20c627f
群组节点系统表定义¶
name | string | No | PRI | 各行同一值,分布式存储基于此key实现全表查询 |
type | string | No | 节点类型(sealer/observer) | |
node_id | string | No | 节点NodeID | |
enable_num | string | No | 该节点类型生效的区块高度 | |
_status_ | string | No | 分布式存储通用字段,“0”可用“1”删除 |
群组系统表接口定义¶
群组系统表实现群组层的白名单机制(对比CA黑名单实现网络的黑名单机制)。群组系统表提供的接口有:
contract ConsensusSystemTable
{
// 修改一节点为共识节点
function addSealer(string nodeID) public returns(int256);
// 修改一节点为观察节点
function addObserver(string nodeID) public returns(int256);
// 把该节点从群组系统表中移除
function remove(string nodeID) public returns(int256);
}
功能展望¶
- 可改配置目前为修改后重启生效,后续可实现动态加载,修改实时生效;
- CA黑名单目前实现了基于节点的黑名单,后续可考虑基于机构的黑名单。
FAQ¶
CA黑名单介绍¶
本文档对黑名单进行介绍性说明,实践方法参见《CA黑名单操作手册》。
名词解释¶
CA黑名单,别称证书拒绝列表(certificate rejected list,简称CRL)。CA黑名单基于config.ini
文件中[crl]
配置的NodeID进行判断,拒绝此NodeID节点发起的连接。
CA黑名单所属的配置类型:
- 基于作用范围(网络配置/账本配置)维度可划分为网络配置,影响整个网络的节点连接建立过程;
- 基于是否可改(可改配置/固定配置)维度可划分为可改配置,内容可改,重启后生效;
- 基于存放位置(本地存储/链上存储)维度可划分为本地存储,内容记录在本地,不存于链上。
核心流程¶
底层实现SSL双向验证。节点在handshake过程中,通过对方提供的证书获取对方节点的nodeID,检查该nodeID是否在自身的CA黑名单。如果在,关闭该connect,继续后续流程。
影响范围¶
- CA黑名单对网络层的P2P节点连接及AMOP功能有显著影响,使之失效;
- 对账本层的共识和同步功能有潜在影响,影响共识及同步消息/数据的转发。
配置格式¶
节点config.ini
配置中增加[crl]
路径([crl]
在配置中可选)。CA黑名单内容为节点NodeID列表,node.X为本节点拒绝连接的对方节点NodeID。CA黑名单的配置格式示例如下。
[crl]
crl.0=4d9752efbb1de1253d1d463a934d34230398e787b3112805728525ed5b9d2ba29e4ad92c6fcde5156ede8baa5aca372a209f94dc8f283c8a4fa63e3787c338a4
crl.1=af57c506be9ae60df8a4a16823fa948a68550a9b6a5624df44afcd3f75ce3afc6bb1416bcb7018e1a22c5ecbd016a80ffa57b4a73adc1aeaff4508666c9b633a
功能展望¶
- CA黑名单目前为修改后重启生效,后续可实现动态加载,修改实时生效;
- CA黑名单目前实现了基于节点的黑名单,后续可考虑基于机构的黑名单。
权限控制¶
1 权限控制介绍¶
与可自由加入退出、自由交易、自由检索的公有链相比,联盟链有准入许可、交易多样化、基于商业上隐私及安全考虑、高稳定性等要求。因此,联盟链在实践过程中需强调“权限”及“控制”的理念。
为体现“权限”及“控制”理念,FISCO BCOS平台基于分布式存储,提出分布式存储权限控制的机制,可以灵活,细粒度的方式进行有效的权限控制,为联盟链的治理提供重要的技术手段。分布式权限控制基于外部账户(tx.origin)的访问机制,对包括合约部署,表的创建,表的写操作(插入、更新和删除)进行权限控制,表的读操作不受权限控制。 在实际操作中,每个账号使用独立且唯一的公私钥对,发起交易时使用其私钥进行签名,接收方可通过公钥验签知道交易具体是由哪个账号发出,实现交易的可控及后续监管的追溯。
2 权限控制规则¶
权限控制规则如下:
- 权限控制的最小粒度为表,基于外部账号进行控制。
- 使用白名单机制,未配置权限的表,默认完全放开,即所有外部账户均有读写权限。
- 权限设置利用权限表(_sys_table_access_)。权限表中设置表名和外部账户地址,则表明该账户对该表有读写权限,设置之外的账户对该表仅有读权限。
3 权限控制分类¶
分布式存储权限控制分为对用户表和系统表的权限控制。用户表指用户合约所创建的表,用户表均可以设置权限。系统表指FISCO BCOS区块链网络内置的表,系统表的设计详见存储文档。系统表的权限控制如下所示:
表名 | 是否生效 | 表存储数据说明 | 权限控制意义 |
---|---|---|---|
_sys_tables_ | 是 | 存储所有表的结构 | 控制部署合约和创建表 |
_sys_table_access_ | 是 | 存储权限控制信息 | 控制权限功能设置 |
_sys_consensus_ | 是 | 存储共识节点和观察节点的列表 | 控制节点类型设置 |
_sys_cns_ | 是 | 存储cns列表 | 控制使用CNS |
_sys_config_ | 是 | 存储系统配置的列表 | 控制系统配置设置 |
_sys_current_state_ | 否 | 存储最新的状态 | – |
_sys_tx_hash_2_block_ | 否 | 存储交易hash到区块号的映射 | – |
_sys_number_2_hash_ | 否 | 存储区块号到区块hash的映射 | – |
_sys_hash_2_block_ | 否 | 存储区块hash到序列化区块的映射 | – |
针对用户表和每个系统表,SDK分别实现三个接口进行权限相关操作:
- 用户表:
- public String addUserTableManager(String tableName, String address): 根据用户表名和外部账号地址设置权限信息。
- public String removeUserTableManager(String tableName, String address): 根据用户表名和外部账号地址去除权限信息。
- public List<AuthorityInfo> queryUserTableManager(String tableName): 根据用户表名查询设置的权限记录列表(每条记录包含外部账号地址和生效块高)。
- _sys_tables_表:
- public String addDeployAndCreateManager(String address): 增加外部账号地址的部署合约和创建用户表权限。
- public String removeDeployAndCreateManager(String address): 移除外部账号地址的部署合约和创建用户表权限。
- public List<AuthorityInfo> queryDeployAndCreateManager(): 查询拥有部署合约和创建用户表权限的权限记录列表。
- _sys_table_access_表:
- public String addAuthorityManager(String address): 增加外部账号地址的管理权限的权限。
- public String removeAuthorityManager(String address): 移除外部账号地址的管理权限的权限。
- public List<AuthorityInfo> queryAuthorityManager(): 查询拥有管理权限的权限记录列表。
- _sys_consensus_表:
- public String addNodeManager(String address): 增加外部账号地址的节点管理权限。
- public String removeNodeManager(String address): 移除外部账号地址的节点管理权限。
- public List<AuthorityInfo> queryNodeManager(): 查询拥有节点管理的权限记录列表。
- _sys_cns_表:
- public String addCNSManager(String address): 增加外部账号地址的使用CNS权限。
- public String removeCNSManager(String address): 移除外部账号地址的使用CNS权限。
- public List<AuthorityInfo> queryCNSManager(): 查询拥有使用CNS的权限记录列表。
- _sys_config_表:
- public String addSysConfig(String address): 增加外部账号地址的系统参数管理权限。
- public String removeSysConfig(String address): 移除外部账号地址的系统参数管理权限。
- public List<AuthorityInfo> querySysConfig(): 查询拥有系统参数管理的权限记录列表。
设置和移除权限接口返回json字符串,包含code和msg字段,当无权限操作时,其code定义-1,msg定义为“non-authorized”。当成功设置权限时,其code为1(增加了1条权限记录),msg为“success”。
3 数据定义¶
权限信息以系统表的方式进行存储,权限表表名为_sys_table_access_,其字段信息定义如下:
字段 | 类型 | 是否为空 | 主键 | 描述 |
---|---|---|---|---|
table_name | string | No | PRI | 表名称 |
address | string | No | 外部账户地址 | |
enable_num | string | No | 权限设置生效区块高度 | |
_status_ | string | No | 分布式存储通用字段,“0”表示可用,“1”表示移除 |
其中,对权限表的插入或更新,当前区块不生效,在当前区块的下一区块生效。状态字段为“0”时,表示权限记录处于正常生效状态,为“1”时表示已删除,即表示权限记录处于失效状态。
4 流程图¶
4.1 用户表权限控制流程¶
外部账户查询表不进行权限控制。当需要更新,增加或移除记录时,将通过查询权限表进行权限控制。流程如下图所示。
4.2 系统表权限控制流程¶
对于sdk层,用户合约不可以直接操作权限表,通过sdk的AuthorityService接口(详见sdk使用文档)和控制台(详见控制台使用文档)可以操作系统表。对于C++底层,当需要操作权限表时,通过AuthorityPreCompiled进行权限表的操作。其中查询权限表不需要检查权限,新增和移除权限表的记录需要检查权限。整个系统内权限相关的增删查将通过AuthorityPreCompiled进行维护。所有权限内容记录在区块链上。交易请求发起后,系统将访问_sys_table_access_表查询该交易发起方是否有对应的权限。如果有权限,执行交易;如果无权限,则返回无权限操作提示。
注: _sys_consensus_表(ConsensusPrecompiled),_sys_cns_表(CNSPrecompiled),_sys_config_表(SystemConfigPrecompiled)控制流程与对权限表的控制流程类似。
RPC¶
RPC(Remote Procedure Call,远程过程调用)是客户端与区块链系统交互的一套协议和接口。用户通过RPC接口可查询区块链相关信息(如块高、区块、节点连接等)和发送交易。
1 名词解释¶
2 模块架构¶
RPC模块负责提供FISCO BCOS的外部接口,客户端通过RPC发送请求,RPC通过调用账本管理模块和p2p模块获取相关响应,并将响应返回给客户端。其中账本管理模块通过多账本机制管理区块链底层的相关模块,具体包括共识模块,同步模块,区块管理模块,交易池模块以及区块验证模块。
3 数据定义¶
3.1 客户端请求¶
客户端请求发送至区块链节点会触发RPC调用,客户端请求包括下列数据成员:
- jsonrpc: 指定JSON-RPC协议版本的字符串,必须准确写为“2.0”。
- method: 调用方法的名称。
- params: 调用方法所需要的参数,方法参数可选。 FISCO BCOS 2.0采用多账本机制,第一个参数如果存在,必须为群组ID。
- id: 已建立客户端的唯一标识id,id必须是一个字符串、数值或NULL空值。如果不包含该成员则被认定为是一个通知。
RPC请求包格式示例:
{"jsonrpc": "2.0", "method": "getBlockNumber", "params": [1], "id": 1}
注:
- 在请求对象中不建议使用NULL作为id值,因为该规范将使用空值认定为未知id的请求。
- 在请求对象中不建议使用小数作为id值,因为具有不确定性。
3.2 服务端响应¶
当发起一个RPC调用时,除通知之外,区块链节点都必须回复响应。响应表示为一个JSON对象,使用以下成员:
- jsonrpc: 指定JSON RPC协议版本的字符串。必须准确写为“2.0”。
- result: 正确结果字段。该成员在响应处理成功时必须包含,当调用方法引起错误时必须不包含该成员。
- error: 错误结果字段。该成员在失败是必须包含,当没有引起错误的时必须不包含该成员。该成员参数值必须为3.3节中定义的对象。
- id: 响应id。该成员必须包含,该成员值必须与对应客户端请求中的id值一致。若检查请求对象的id错误(例如参数错误或无效请求),则该值必须为空值。
RPC响应包格式示例:
{"jsonrpc": "2.0", "result": "0x1", "id": 1}
注: 服务端响应必须包含result或error成员,但两个成员不能同时包含。
3.3 错误对象¶
当一个RPC调用遇到错误时,返回的响应对象必须包含error错误结果字段,该字段有下列成员参数:
- code: 使用数值表示该异常的错误类型,必须为整数。
- message: 对该错误的简单描述字符串。
- data: 包含关于错误附加信息的基本类型或结构化类型,该成员可选。
错误对象包含两类错误码,分别是JSON RPC标准错误码和FISCO BCOS RPC错误码。
3.3.1 JSON RPC标准错误码¶
标准错误码及其对应的含义如下:
code | message | 含义 |
---|---|---|
-32600 | INVALID_JSON_REQUEST | 发送无效的请求对象 |
-32601 | METHOD_NOT_FOUND | 该方法不存在或无效 |
-32602 | INVALID_PARAMS | 无效的方法参数 |
-32603 | INTERNAL ERROR | 内部调用错误 |
-32604 | PROCEDURE_IS_METHOD | 请求未提供id字段 |
-32700 | JSON_PARSE_ERROR | 服务端接收到的json无法解析 |
3.3.2 FISCO BCOS RPC错误码¶
FISCO BCOS RPC接口错误码及其对应的含义如下:
code | message | 含义 |
---|---|---|
1 | GroupID does not exist | GroupID不存在 |
2 | Response json parse error | JSON RPC获取的json数据解析错误 |
3 | BlockHash does not exist | 区块哈希不存在 |
4 | BlockNumber does not exist | 区块高度不存在 |
5 | TransactionIndex is out of range | 交易索引越界 |
6 | Call needs a ‘from’ field | call接口需要提供from字段 |
7 | Only pbft consensus supports the view property | getPbftView接口,只有pbft共识机制有view属性 |
8 | Invalid System Config | getSystemConfigByKey接口,查询无效的key |
9 | Don’t send requests to this group, the node doesn’t belong to the group | 非群组内节点发起无效的请求 |
4 RPC接口的设计¶
FISCO BCOS提供丰富的RPC接口供客户端调用。其中分为3类:
- 以get开头命名的查询接口:例如
[getBlockNumber]
接口,查询最新的区块高度。 [sendRawTransaction]
接口: 执行一笔签名的交易,将等待区块链共识后才返回响应。[call]
接口: 执行一个请求将不会创建一笔交易,不需要区块链共识,而是获取响应立刻返回。
其他特性¶
为了提供更好的智能合约调用体验、支持更高的安全性,FISCO BCOS引入了合约命名服务(Contract Name Service, CNS)、国密算法和落盘加密特性。
- 合约命名服务(Contract Name Service, CNS)
以太坊基于智能合约地址调用合约,存在如下问题:
- 合约abi为较长的JSON字符串,调用方不需直接感知
- 合约地址为20字节的魔数,不方便记忆,若丢失后将导致合约不可访问
- 约重新部署后,一个或多个调用方都需更新合约地址
- 不便于进行版本管理以及合约灰度升级
FISCO BCOS引入的合约命名服务CNS通过提供链上合约名称与合约地址映射关系的记录及相应的查询功能,方便调用者通过记忆简单的合约名来实现对链上合约的调用。
- 国密算法
为了充分支持国产密码学算法,FISCO BCOS基于 国产密码学标准 ,实现了国密加解密、签名、验签、哈希算法、国密SSL通信协议,并将其集成到FISCO BCOS平台中,实现对 国家密码局认定的商用密码 的完全支持。
- 落盘加密特性
考虑到联盟链的架构中,数据在联盟链的各个机构内是可见的,FISCO BCOS引入了落盘加密特性,对存储到节点数据库中的数据进行加密,并引入Key Center保存加密密钥,保障了节点数据的机密性。
CNS方案¶
概述¶
调用以太坊智能合约的流程包括:
- 编写合约;
- 编译合约得到合约接口abi描述;
- 部署合约得到合约地址address;
- 封装合约的abi和地址,通过SDK等工具实现对合约的调用。
从合约调用流程可知,调用之前必须准备合约abi以及合约地址address。这种使用方式存在以下的问题:
- 合约abi为较长的JSON字符串,调用方不需直接感知;
- 合约地址为20字节的魔数,不方便记忆,若丢失后将导致合约不可访问;
- 合约重新部署后,一个或多个调用方都需更新合约地址;
- 不便于进行版本管理以及合约灰度升级。
为解决以上问题,给调用者提供良好的智能合约调用体验,FISCO BCOS提出CNS合约命名服务。
名词解释¶
- CNS(Contract Name Service)通过提供链上合约名称与合约地址映射关系的记录及相应的查询功能,方便调用者通过记忆简单的合约名来实现对链上合约的调用。
- CNS信息为合约名称、合约版本、合约地址和合约abi
- CNS表用于存储CNS信息
CNS对比以太坊原有调用方式的优势¶
- 简化调用合约方式;
- 合约升级对调用者透明,支持合约灰度升级。
对标ENS¶
ENS (Ethereum Name Service) ,以太坊名称服务。
ENS的功能类似我们较熟悉的DNS(Domain Name Service)域名系统,但提供的不是Internet网址,而是将以太坊(Ethereum)合约地址和钱包地址以xxxxxx.eth网址的方式表示,用于存取合约或转账。两者相比:
- ENS映射的地址类型包括合约地址及钱包地址,CNS可支持,当地址类型为钱包地址时合约abi为空。
- ENS有竞拍功能,CNS不需支持。
- ENS支持多级域名,CNS不需支持。
核心流程¶
用户调用SDK部署合约及调用合约流程如下:
- 部署合约时,SDK生成合约对应的Java类,调用类的deploy接口发布合约获得合约地址,然后调用CNS合约insert接口上链CNS信息。
- 调用合约时,SDK引入合约的Java类,并加载实例化。load加载接口可传入合约地址(原有以太坊方式)或合约名称和合约版本的组合(CNS方式),SDK处理CNS方式时通过调用CNS模块查询链上信息来获取合约地址。
- 对于缺少版本号的合约调用,由SDK实现默认调用合约的最新版本。
- 上链的合约abi信息属于可选字段。
数据结构¶
CNS表结构¶
CNS信息以系统表的方式进行存储,各账本独立。CNS表定义如下:
name | string | No | PRI | 合约名称,name和version为联合主键 |
version | string | No | 合约名称,name和version为联合主键 | |
address | string | No | 合约地址 | |
abi | string | YES | 合约abi | |
_status_ | string | No | 分布式存储通用字段,“0”可用“1”删除 |
合约接口¶
pragma solidity ^0.4.2;
contract CNS
{
function insert(string name, string version, string addr, string abi) public returns(uint256);
function selectByName(string name) public constant returns(string);
function selectByNameAndVersion(string name, string version) public constant returns(string);
}
- CNS合约不暴露给用户,为SDK与底层CNS表的交互接口。
- insert接口提供CNS信息上链的功能,接口四个参数分别为合约名称name、合约版本version、合约地址addr和合约ABI信息abi。SDK调用接口需判断name和version的组合与数据库原有记录是否重复,在不重复的前提下才能发起上链交易。节点在执行交易时,precompiled逻辑会Double Check,发现数据重复就直接抛弃该交易。insert接口对CNS表的内容只增不改。
- selectByName接口参数为合约名称name,返回表中所有基于该合约的不同version记录。
- selectByNameAndVersion接口参数为合约名称name和合约版本version,返回表中该合约该版本的唯一地址。
precompiled合约是FISCO BCOS底层通过C++实现的一种高效智能合约,用于FISCO BCOS底层的系统信息配置与管理。引入precompiled逻辑后,FISCO BCOS节点执行交易的流程如下:
CNS合约属于precompiled合约类型,节点将通过内置C++代码逻辑实现对CNS表的插入和查询操作,不经EVM执行,因此CNS合约只提供了函数接口描述而没有函数实现。预置CNS合约的precompiled地址为0x1004。
selectByName和selectByNameAndVersion接口返回的string为Json格式,示例如下:
[
{
"name" : "Ok",
"version" : "1.0",
"address" : "0x420f853b49838bd3e9466c85a4cc3428c960dde2",
"abi" : "[{\"constant\":false,\"inputs\":[{\"name\":\"num\",\"type\":\"uint256\"}],\"name\":\"trans\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"co
nstant\":true,\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[],\"payable\":false,\
"type\":\"constructor\"}]"
},
{
"name" : "Ok",
"version" : "2.0",
"address" : "0x420f853b49838bd3e9466c85a4cc3428c960dde2",
"abi" : "[{\"constant\":false,\"inputs\":[{\"name\":\"num\",\"type\":\"uint256\"}],\"name\":\"trans\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"co
nstant\":true,\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[],\"payable\":false,\
"type\":\"constructor\"}]"
}
]
SDK_API¶
SDK开发者可使用org.fisco.bcos.web3j.precompile.cns
中以下两接口实现CNS的注册及查询功能。
registerCns¶
- 描述:public TransactionReceipt registerCns(String name, String version, String addr, String abi)
- 功能:上链合约信息
- 参数:name——合约名,version——合约版本,addr——合约地址,abi——合约abi
- 返回:上链交易回执,回执中含上链结果信息及错误信息(如有)
resolve¶
- 描述:public String resolve(String contractNameAndVersion)
- 功能:基于合约名和合约版本查询合约地址
- 参数:contractNameAndVersion——合约名+合约版本信息
- 返回:合约地址,如无参数指定版本的合约信息,接口抛出异常
- 说明:contractNameAndVersion通过
:
来分割合约名和合约版本,当缺少合约版本时,SDK默认调用使用合约的最新版本进行查询
注意:
- 在调用接口前,需将sol合约转换Java类,并将生成的Java类以及abi、bin文件置于正确的目录,详细使用方法请参考《SDK》;
- 两个接口的使用例子可参考ConsoleImpl.java中的deployByCNS和callByCNS接口实现。
国密支持方案¶
设计目标¶
为了充分支持国产密码学算法,金链盟基于国产密码学标准,实现了国密加解密、签名、验签、哈希算法、国密SSL通信协议,并将其集成到FISCO BCOS平台中,实现了对国家密码局认定的商用密码的完全支持。
国密版fisco bcos与标准版在数据结构上的差异如下:
- 交易长度:国密版fisco bcos的签名长度为1024bits(128字节,其中公钥512bits,签名长度512bits),标准版长度为520bits(其中标识符8bits,签名长度512bits)
- 国密双证书模式:国密fisco bcos在进行p2p链接时拥有两条证书,分别为签名证书gmnode.crt和加密证书gmennode.crt。同时对应两套私钥gmnode.key和gmennode.key。
- 国密solidity编译器:在编译智能合约时,solidity编译器使用国密SM4消息摘要算法
系统框架¶
系统整体框架如下图所示:
下表以签名、验签、哈希算法等为出发点,对比了国密版FISCO-BCOS和普通版FISCO-BCOS:
(注:国密算法SM2, SM3, SM4均基于国产密码学标准开发)
算法类型 | 国密版FISCO BCOS | 标准版FISCO BCOS |
---|---|---|
签名算法 | SM2 (公私钥长度:512 bits, 256 bits) | ECDSA (公私钥长度: 512 bits, 256 bits) |
哈希算法 | SM3 (哈希串长度: 256 bits) | SHA3 (哈希串长度: 256 bits) |
对称加解密算法 | SM4 (对称密钥长度: 128 bits) | AES (加密秘钥长度: 256 bits) |
特性说明¶
为了彻底支持国密,国密版FISCO BCOS将交易签名验签、p2p网络连接、节点连接、数据落盘加密等底层模块的密码学算法均替换为国密算法,国密版FISCO-BCOS与标准版主要特性对比如下:
标准版FISCO-BCOS | 国密版FISCO BCOS | |
---|---|---|
SSL链接 | Openssl TLSv1.2协议 | 国密TLSv1.1协议 |
签名验证 | ECDSA签名算法 | SM2签名算法 |
消息摘要算法 | SHA-256 SHA-3 | SM3消息摘要算法 |
落盘加密算法 | AES-256加密算法 | SM4加密算法 |
落盘加密¶
背景介绍¶
在联盟链的架构中,机构和机构之间搭建一条区块链,数据在联盟链的各个机构内是可见的。
在某些数据安全性要求较高的场景下,联盟内部的成员并不希望联盟之外的机构能够获取联盟链上的数据。此时,就需要对联盟链上的数据进行访问控制。
联盟链数据的访问控制,主要分为两个方面
- 链上通信数据的访问控制
- 节点存储数据的访问控制
对于链上通信数据的访问控制,FISCO BCOS是通过节点证书和SSL来完成。此处主要介绍的是节点存储数据的访问控制,即落盘加密。
主要思想¶
落盘加密是在机构内部进行的。在机构的内网环境中,每个机构独立的对节点的硬盘数据进行加密。当节点所在机器的硬盘被带离机构,并让节点在机构内网之外的网络启动,硬盘数据将无法解密,节点无法启动。进而无法盗取联盟链上的数据。
方案架构¶
落盘加密是在机构内部进行的,每个机构独立管理自己硬盘数据的安全。内网中,每个节点的硬盘数据是被加密的。所有加密数据的访问权限,通过Key Center来管理。Key Center是部署在机构内网内,专门管理节点硬盘数据访问秘钥的服务,外网无法访问。当内网的节点启动时,从Key Center处获取加密数据的访问秘钥,来对自身的加密数据进行访问。
加密保护的对象包括:
- 节点本地存储的数据库:leveldb
- 节点私钥:node.key,gmnode.key(国密)
具体实现¶
具体的实现过程,是通过节点自身持有的秘钥(dataKey)和Key Center管理的全局秘钥(superKey)来完成的。
节点
- 节点用自己的dataKey,对自身加密的数据(Encrypted Space)进行加解密。
- 节点本身不会在本地磁盘中存储dataKey,而是存储dataKey被加密后的cipherDataKey。
- 节点启动时,拿cipherDataKey向KeyCenter请求,获取dataKey。
- dataKey只在节点的内存中,当节点关闭后,dataKey自动丢弃。
Key Center
持有全局的superKey,负责对所有节点启动时的授权请求进行响应,授权。
- Key Center必须实时在线,响应节点的启动请求。
- 当节点启动时,发来cipherDataKey,Key Center用superKey对cipherDataKey进行解密,若解密成功,就将节点的dataK返回给节点。
- Key Center只能在内网访问,机构内的外网无法访问Key Center.
方案流程¶
方案流程分为节点初始配置和节点安全运行。
节点初始配置¶
节点启动前,需要为节点配置dataKey
注意:节点在生成后,启动前,必须决定好是否采用落盘加密,一旦节点配置成功,并正常启动,将无法切换状态。
(1)管理员定义好节点的的dataKey,并将dataKey发送给Key Center,从Key Center处获取cipherDataKey。
(2)将cipherDataKey配置到节点的配置文件中
(3)启动节点
节点安全运行¶
节点启动时,会通过Key Center,获取本地数据访问的秘钥dataKey。
(1)节点启动,从配置文件中读取cipherDataKey,并发送给Key Center。
(2)Key Center收到cipherDataKey,用superKey解密cipherDataKey,若解密成功,则将解密后的dataKey返回给节点。
(3)节点拿到dataKey,用dataKey对本地的数据(Encrypted Space)进行交互。从Encrypted Space读取的数据,用dataKey解密获取真实数据。要写入Encrypted Space的数据,先用dataKey加密,再写入。
合约开发¶
本章介绍FISCO BCOS智能合约开发相关知识。FISCO BCOS平台目前支持Precompiled、CRUD、Solidity三种智能合约形式。
Precompiled合约使用C++开发,内置于FISCO BCOS平台,相比于Solidity合约具有更好的性能,但由于是编译时确定,所以适用于逻辑固定但需要共识的场景,例如群组配置。
CRUD合约通过在Solidity合约中支持分布式存储Precompiled合约,可以实现将Solidity合约中数据存储在FISCO BCOS平台AMDB的表结构中,实现合约逻辑与数据的分离。
Solidity与以太坊相同,支持最新版本。
Precompile合约开发¶
一. 简介¶
预编译合约(precompiled contract)是以太坊原生支持的一项功能, FISCO-BCOS在此基础上发展了一套自己的precompiled框架, 用来突破EVM的诸多限制, 并且大大的增强了自身的拓展性。本文作为一篇入门指导, 旨在指引用户如何在FISCO-BCOS实现自己的precompiled合约, 并且能够与EVM或者客户端进行交互。
二. 实现precompiled合约¶
2.1 基本步骤¶
- 定义合约接口precompiled合约使用的接口, 与solidity的接口定义格式完全相同, 因此定义接口时可以先写个对应的solidity合约文件, 将接口的函数体都置空即可.
- 定义表的结构不涉及存储操作可以省略该步骤.FISCO-BCOS底层存储结构参见链接.
- 选择入口地址solidity或者precompiled调用根据地址来区分, 目前的地址分配如下:
保留地址 | 系统配置以及保留地址 | 用户使用地址 | 临时precompiled合约地址 | solidity合约地址 |
---|---|---|---|---|
0x0000 - 0x0fff | 0x1000 - 0x5000 | 0x5001 - 0xffff | 0x10000+ | 其他 |
用户分配地址空间为0x5001 - 0xffff, 用户可以为此范围内为新增的precompiled合约选一个地址, 不同precompiled的地址不要冲突即可.
- 实现调用逻辑新增的precompiled合约需要继承Precompiled合约, 在重载的call函数中实现precompiled合约各个接口调用的行为.
// FISCO-BCOS/libblockverifier/Precompiled.h
class Precompiled
{
... begin 省略 begin ....
virtual bytes call(std::shared_ptr<ExecutiveContext> context, bytesConstRef param,
Address const& origin = Address()) = 0;
... end 省略 end ...
};
- 注册合约到执行上下文
目前已经注册的precompiled合约:
// FISCO-BCOS/libblockverifier/ExecutiveContextFactory.cpp
// ExecutiveContextFactory::initExecutiveContext
auto tableFactoryPrecompiled = std::make_shared<dev::blockverifier::TableFactoryPrecompiled>();
tableFactoryPrecompiled->setMemoryTableFactory(memoryTableFactory); // table precompiled
context->setAddress2Precompiled(Address(0x1000), std::make_shared<dev::precompiled::SystemConfigPrecompiled>()); // 系统配置
context->setAddress2Precompiled(Address(0x1001),tableFactoryPrecompiled);
context->setAddress2Precompiled(Address(0x1002), std::make_shared<dev::precompiled::CRUDPrecompiled>()); //CRUD
context->setAddress2Precompiled(Address(0x1003), std::make_shared<dev::precompiled::ConsensusPrecompiled>()); //共识节点管理
context->setAddress2Precompiled(Address(0x1004), std::make_shared<dev::precompiled::CNSPrecompiled>()); //cns数据管理
context->setAddress2Precompiled(Address(0x1005), std::make_shared<dev::precompiled::AuthorityPrecompiled>()); //权限管理
2.2 step by step sample¶
本章节通过precompiled方式实现一个HelloWorld.sol(合约如下)的功能, 通过step by step带大家对precompiled合约编写有个直观的了解。该示例的c++源码位于:FISCO-BCOS/examples/HelloWorldPrecompiled.h HelloWorldPrecompiled.cpp
//HelloWorld.sol
pragma solidity ^0.4.25;
contract HelloWorld {
string m;
function HelloWorld() {
m = "Hello World!";
}
function get() public constant returns(string) {
return m;
}
function set(string _m) public {
m = _m;
}
}
2.2.1 合约接口¶
需要实现HelloWorld.sol的功能, 接口与HelloWorld.sol接口相同
contract HelloWorld {
function get() public constant returns(string);
function set(string _m);
}
2.2.2 表结构¶
HelloWorld中需要存储单个字符串, 因此需要设计表结构.
表名: __test_hello_world__表结构: | key | value | | ——— | ———– | | hello_key | hello_value |
该表只存储一对键值对, key字段为hello_key, value字段为hello_value 存储对应的字符串值, 可以通过set(string)接口修改, 通过get()接口获取.
2.2.3 入口地址¶
0x5001
2.2.4 实现调用逻辑¶
实现HelloWorldPrecompiled类
//file HelloWorldPrecompiled.h
class HelloWorldPrecompiled : public dev::blockverifier::Precompiled
{
public:
... begin 省略 begin ...
bytes call(dev::blockverifier::ExecutiveContext::Ptr context, bytesConstRef param,
Address const& origin = Address()) override;
... end 省略 end ...
};
//file HelloWorldPrecompiled.cpp
bytes HelloWorldPrecompiled::call(
dev::blockverifier::ExecutiveContext::Ptr context, bytesConstRef param, Address const& origin)
{
// 函数名解析
uint32_t func = getParamFunc(param);
// 参数解析
bytesConstRef data = getParamData(param);
// 返回值
bytes out;
if (func == name2Selector[HELLO_WORLD_METHOD_GET])
{ // get() function call operation
}
else if (func == name2Selector[DAG_TRANSFER_METHOD_SAV_STR_UINT])
{ // set(string) function call operation
}
else
{ // unkown function call
}
return out;
}
2.2.5 注册合约¶
// FISCO-BCOS/libblockverifier/ExecutiveContextFactory.cpp
// ExecutiveContextFactory::initExecutiveContext
context->setAddress2Precompiled(Address(0x5001), std::make_shared<dev::precompiled::HelloWorldPrecompiled>()); //HelloWorld precompiled 注册
2.2.6 其他流程¶
编译, 搭链, 测试
CRUD合约开发¶
访问 AMDB 需要使用 AMDB 专用的智能合约 Table.sol 接口,该接口是数据库合约,可以创建表,并对表进行增删改查操作。
Table.sol文件代码如下:
contract TableFactory {
function openTable(string) public constant returns (Table); //打开表
function createTable(string,string,string) public constant returns(Table); //创建表
}
//查询条件
contract Condition {
function EQ(string, int);
function EQ(string, string);
function NE(string, int);
function NE(string, string);
function GT(string, int);
function GE(string, int);
function LT(string, int);
function LE(string, int);
function limit(int);
function limit(int, int);
}
//单条数据记录
contract Entry {
function getInt(string) public constant returns(int);
function getAddress(string) public constant returns(address);
function getBytes64(string) public constant returns(byte[64]);
function getBytes32(string) public constant returns(bytes32);
function set(string, int) public;
function set(string, string) public;
}
//数据记录集
contract Entries {
function get(int) public constant returns(Entry);
function size() public constant returns(int);
}
//Table主类
contract Table {
//查询接口
function select(string, Condition) public constant returns(Entries);
//插入接口
function insert(string, Entry) public returns(int);
//更新接口
function update(string, Entry, Condition) public returns(int);
//删除接口
function remove(string, Condition) public returns(int);
function newEntry() public constant returns(Entry);
function newCondition() public constant returns(Condition);
}
提供一个合约案例 TableTest.sol,代码如下:
import "./Table.sol";
contract TableTest {
event selectResult(bytes32 name, int item_id, bytes32 item_name);
event insertResult(int count);
event updateResult(int count);
event removeResult(int count);
//创建表
function create() public {
TableFactory tf = TableFactory(0x1001); //TableFactory的地址固定为0x1001
tf.createTable("t_test", "name", "item_id,item_name");
}
//查询数据
function select(string name) public constant returns(bytes32[], int[], bytes32[]){
TableFactory tf = TableFactory(0x1001);
Table table = tf.openTable("t_test");
Condition condition = table.newCondition();
//condition.EQ("name", name);
Entries entries = table.select(name, condition);
bytes32[] memory user_name_bytes_list = new bytes32[](uint256(entries.size()));
int[] memory item_id_list = new int[](uint256(entries.size()));
bytes32[] memory item_name_bytes_list = new bytes32[](uint256(entries.size()));
for(int i=0; i<entries.size(); ++i) {
Entry entry = entries.get(i);
user_name_bytes_list[uint256(i)] = entry.getBytes32("name");
item_id_list[uint256(i)] = entry.getInt("item_id");
item_name_bytes_list[uint256(i)] = entry.getBytes32("item_name");
selectResult(user_name_bytes_list[uint256(i)], item_id_list[uint256(i)], item_name_bytes_list[uint256(i)]);
}
return (user_name_bytes_list, item_id_list, item_name_bytes_list);
}
//插入数据
function insert(string name, int item_id, string item_name) public returns(int) {
TableFactory tf = TableFactory(0x1001);
Table table = tf.openTable("t_test");
Entry entry = table.newEntry();
entry.set("name", name);
entry.set("item_id", item_id);
entry.set("item_name", item_name);
int count = table.insert(name, entry);
insertResult(count);
return count;
}
//更新数据
function update(string name, int item_id, string item_name) public returns(int) {
TableFactory tf = TableFactory(0x1001);
Table table = tf.openTable("t_test");
Entry entry = table.newEntry();
entry.set("item_name", item_name);
Condition condition = table.newCondition();
condition.EQ("name", name);
condition.EQ("item_id", item_id);
int count = table.update(name, entry, condition);
updateResult(count);
return count;
}
//删除数据
function remove(string name, int item_id) public returns(int){
TableFactory tf = TableFactory(0x1001);
Table table = tf.openTable("t_test");
Condition condition = table.newCondition();
condition.EQ("name", name);
condition.EQ("item_id", item_id);
int count = table.remove(name, condition);
removeResult(count);
return count;
}
}
TableTest.sol 调用了 AMDB 专用的智能合约 Table.sol,实现的是创建用户表 t_test,并对 t_test 表进行增删改查的功能。
注意: 客户端需要调用转换为 Java 文件的合约代码,需要将 TableTest.sol 和 Table.sol 放入 web3sdk 的 src/test/resources/contract 目录下,通过 web3sdk 的编译脚本生成 TableTest.java。
企业工具¶
fisco-generator 是针对多机构组网的企业区块链部署工具。实现了一种适用于多机构部署维护多群组联盟链的方式.
- 在使用本工具时,所有机构间只需要共享节点的证书,节点的私钥由各机构自己生成维护,保证节点私钥不出内网,确保机构间节点的安全性;
- 同时提供了多种联盟链多群组间的部署方式,可以降低机构间生成与维护区块链的复杂度;
- 基于本工具,多机构间可以通过交换数字证书,对等、安全的部署自己的节点。节点生成完成后导入私钥,启动属于本机构的节点。

基本介绍¶
设计背景¶
现有的联盟链运维管理工具在初始化时都没有考虑联盟链间多个企业地位对等的诉求。联盟链在初始化时,需要协商创世节点中包含的节点信息。因此谁来生成这些信息就显得十分重要。现有做法为某一机构生成自己的节点信息,启动区块链,再加入其它机构的节点;或是由权威第三方机构直接生成所有机构内的节点信息,并将安装包发送给各机构内。这些做法都不满足联盟链中各机构对等,安全的诉求。
FISCO BCOS引入了隐私性和可扩展性更强的多群组架构。群组架构易用性更强,使得企业建链像建微信群一样便利;在群组架构下,群组间数据、交易相互隔离,每个群组运行独立的共识算法,可满足区块链场景中的隐私保护需求。
在多群组架构中,如何设计一种有效的节点扩容,群组划分方式,是使用多群组架构联盟链必须考虑的问题。如何保证企业间如何对等、安全、隐私地新建群组。新建群组之后如何保证节点可靠,有效的运行;群组账本的隐私性和安全性,以及企业建立群组、使用群组操作的隐私性都需要一个有效的方式。
设计思路¶
FISCO Generator从上述背景出发,根据灵活、安全、易用、对等的原则,从不同机构对等部署、新建群组的角度考虑,设计了解决上述问题的解决方案
在使用时,本工具会根据用户配置的配置文件(包含节点的hostip,端口号等信息),及meta目录下用户存放的符合规范的节点证书,生成节点所需启动的安装包。
用户生成安装包后,导入私钥,既可以启动节点。同时允许可以使用本工具及目录下的证书,生成新group的相关配置,用户将生成的配置文件拷贝至对应节点下,节点会进行group组网
硬件要求¶
由于多群组共享网络带宽、CPU和内存资源,因此为了保证服务的稳定性,一台机器上不推荐配置过多群组。
下表是单群组单节点推荐的配置,节点耗费资源与群组个数呈线性关系,您可根据实际的业务需求和机器资源,合理地配置群组数目:
配置 | 最低配置 | 推荐配置 |
---|---|---|
CPU | 1.5GHz | 2.4GHz |
内存 | 1GB | 8GB |
核心 | 1核 | 4核 |
带宽 | 1Mb | 10Mb |
安装¶
fisco generator依赖python, openssl以及curl, nc工具。使用前请检查是否满足依赖,同时,需要先满足fisco bcos启动时的条件,参考依赖安装
$ git clone https://github.com/FISCO-BCOS/generator.git
$ cd generator
$ bash ./scripts/install.sh
$ ./generator -h
快速开始¶
$ git clone https://github.com/FISCO-BCOS/generator.git
$ cd generator
$ ./generator --demo
此操作会在执行目前的所有操作
- 按照./conf/mchain.ini中的配置在./meta下生成证书
- 按照./conf/mchain.ini的配置在./data下生成安装包
- 拷贝./meta下的私钥至./data的安装包(还做了替换config.ini的操作)
- 按照./conf/expand.ini中的配置在./meta下生成证书
- 按照./conf/expand.ini的配置在./expand下生成扩容安装包
- 按照./conf/mgroup.ini的配置在./data下生成group2的相关配置
操作完成后,用户可以:
- 在./data下运行start_all.sh启动所有节点,并查看节点配置
- 在./expand下查看扩容生成的节点
- 在./data下查看group2的相关信息
配置文件¶
fisco generator的配置文件在./conf文件夹下,共有三个配置文件,mchain.ini、mexpand.ini和mgroup.ini。分别对应新建节点及群组、扩容新节点加入现有群组、节点划分新群组三种操作。
meta文件夹¶
meta文件夹下需要存放生成节点的证书,包括fisco bcos二进制文件(或者用户指定版本,待讨论)、链证书ca.crt、节点证书。
证书的存放格式需要为cert_p2pip_port.crt的格式,如cert_127.0.0.1_30300.crt。节点证书需要已经拼装好agency.crt
mchain.ini¶
通过修改mchain.ini的配置,用户可以使用–build命令在指定文件夹下生成节点不含私钥的安装包。用户配置的每个section[node]即为生成好的链的安装包
[node0]
p2p_ip=127.0.0.1 # 节点p2p通信ip
rpc_ip=127.0.0.1 # 节点与sdk通信ip
p2p_listen_port=30300 # 节点通信端口
channel_listen_port=20200 # sdk与节点通信端口
jsonrpc_listen_port=8545 # 节点rpc端口
[node1]
p2p_ip=127.0.0.1
rpc_ip=127.0.0.1
p2p_listen_port=30301
channel_listen_port=20201
jsonrpc_listen_port=8546
[node2]
p2p_ip=127.0.0.1
rpc_ip=127.0.0.1
p2p_listen_port=30302
channel_listen_port=20202
jsonrpc_listen_port=8547
[node3]
p2p_ip=127.0.0.1
rpc_ip=127.0.0.1
p2p_listen_port=30303
channel_listen_port=20203
jsonrpc_listen_port=8548
[group]
group_id=1
上述配置在执行–build命令后会在指定目录下生成名为node_127.0.0.1_30300、node_127.0.0.1_30301、node_127.0.0.1_30302、node_127.0.0.1_30303的不含节点私钥的安装包。节点处于group1中。
mexpand.ini¶
与mchain.ini配置相似,通过修改mexpand.ini的配置,用户可以使用--expand命令在指定文件夹下生成节点不含私钥的安装包。用户配置的每个section[node]即为生成好的链的安装包
[node0]
p2p_ip=127.0.0.1 # 节点p2p通信ip
rpc_ip=127.0.0.1 # 节点与sdk通信ip
p2p_listen_port=30304 # 节点通信端口
channel_listen_port=20205 # sdk与节点通信端口
jsonrpc_listen_port=8549 # 节点rpc端口
[node1]
p2p_ip=127.0.0.1
rpc_ip=127.0.0.1
p2p_listen_port=30305
channel_listen_port=20205
jsonrpc_listen_port=8550
[group]
group_id=1
上述配置在执行–build命令后会在指定目录下生成名为node_127.0.0.1_30304、node_127.0.0.1_30305的不含节点私钥的安装包。节点包含group1中配置文件,但是不在群组内,需要group1中的节点发交易允许新节点入网。
mgroup.ini¶
通过修改mgourp.ini的配置,用户可以在指定目录下生成新群组的相关配置,如group.2.ini和group.2.genesis
[group]
; group : group index
group_id=2 #群组序号
member0=127.0.0.1:30300 # 新群组成员
member1=127.0.0.1:30302 # 新群组成员
member2=127.0.0.1:30303 # 新群组成员
操作手册¶
- fisco generator 目前支持的功能如下:
- 支持用户导入证书后部署新节点及新群组; 支持用户利用现有配置扩容新节点加入现有群组; 支持用户根据相关配置文件生成对应证书及私钥; 支持用户导入私钥至对应文件夹等相关操作
基础操作¶
部署新节点及群组 –build¶
– build命令为部署新节点及群组的操作
指定参数为安装包输出路径,用户需配置mchain.ini,并在meta下配置相关节点的证书 —-可用于区块链生成后,创建新群组和新节点的场景
操作完成后,给定节点证书,和节点信息(端口号,ip),生成安装包,并加入一个群里,将打好的包生成到指定目录
操作示例
$ cp node0/node.crt ./meta/cert_127.0.0.1_30300.crt
...
$ cp noden/node.crt ./meta/cert_127.0.0.1_3030n.crt
$ vim ./conf/mchain.ini
$ ./generator --build ~/mydata
程序执行完成后,会在~/mydata文件夹下生成多个名为node_hostip_port的文件夹,推送到对应服务器解压后,拷贝私钥到节点conf下即可启动节点
扩容新节点 –expand¶
–expand命令为扩容新节点加入现有群组
nargs=2, 指定参数1.存放有原有群组信息的路径,2.生成安装包的路径 用户需配置mexpand.ini
给定原有group中节点的配置,和新节点的证书,生成安装包
$ vim ./conf/mexpand.ini
$ cp node0/node.crt ./meta/cert_127.0.0.1_30307.crt
$ cp /tmp/config.ini /tmp/group.1.genesis /tmp/group.1.ini ./expand
$ ./generator --expand ./expand ~/mydata
程序执行完成后,会在~/mydata文件夹下生成名为node_127.0.0.1_30307的文件夹,推送到对应服务器解压后,拷贝私钥到conf下即可启动节点
节点正常启动后,使用sdk将节点加入群组,完成扩容操作
划分新群组 –create¶
–create命令为已有节点新建群组的操作
指定参数为生成的group.i.genesis和group.i.ini的存放路径
此命令会根据用户在meta下配置的证书,在指定目录生成新群组的配置
操作范例
$ cp node0/node.crt ./meta/cert_127.0.0.1_3030n.crt
...
$ vim ./conf/group.ini
$ ./generator --create ~/mydata
程序执行完成后,会在~/mydata文件夹下生成mgroup.ini中配置的group.i.genesis和group.i.ini
用户将生成的group.i.genesis和group.i.ini拷贝至对应节点的conf文件夹下,即可完成新群组划分操作
证书生成相关命令¶
fisco generator提供自签证书操作。示例如下:
生成根证书 –chainca命令¶
用户可以指定目录,生成根证书
$ ./genrator --chainca ./dir_chain_ca(SET)
执行完成后用户可以在指定文件夹下看到根证书ca.crt 和私钥ca.key。
生成机构证书 –agencyca命令¶
用户可以指定机构证书目录,链证书存放目录和机构名称,生成机构证书
$ ./genrator --agencyca ./dir_agency_ca(SET) ./chain_ca_dir The_Agency_Name
执行完成后可以在./dir_agency_ca(SET)路径下生成名为The_Agency_Name的文件夹,包含相应的机构证书
生成sdk证书 –sdkca¶
用户可以指定sdk存放目录,机构证书存放目录,生成sdk证书
$ ./genrator --sdkca ./dir_sdk_ca(SET) ./dir_agency_ca
执行完成后可以在./dir_sdk_ca(SET)路径下生成名为sdk的文件夹,包含相应的sdk证书
生成节点证书 –nodeca¶
用户可以指定机构证书目录,节点存放目录和节点名称,生成节点证书
$ ./genrator --nodeca ./agency_dir node_dir(SET) node_name
执行完成后可以在node_dir(SET) 路径下生成节点证书
根据配置文件生成相应证书¶
fisco generator支持用户根据配置文件夹生成相应证书与相应私钥
–certbuild¶
用户使用此命令,可以在指定目录下根据mchain.ini的相关配置生成相应节点证书与私钥,并放置生成的节点证书与./meta文件夹下,用户生成安装包后可以结合[–deploykey]命令导入私钥到安装包启动节点
注意 此命令会根据meta目录下存放的ca.crt和ca.key生成相应的节点证书,如果没有根证书会自动生成
$ ./genrator --certbuild ./cert
执行完成后会在./cert文件夹下生成节点的相关证书与私钥,并拷贝节点证书放置于./meta下
–certexpand¶
用户使用此命令,可以在指定目录下根据mexpand.ini的相关配置生成相应节点证书与私钥,并放置生成的节点证书与./meta文件夹下,用户生成安装包后可以结合[–deploykey]命令导入私钥到安装包启动节点
$ ./genrator --certexpand ./cert
注意 由于扩容节点与链本身节点的根证书必须相同,此命令会根据meta目录下存放的(ca.crt和ca.key)或(agency.crt和agency.key)生成相应的节点证书,如果没有根证书抛出异常
执行完成后会在./cert文件夹下生成节点的相关证书与私钥,并拷贝节点证书放置于./meta下
其他命令¶
-h/–help¶
用户可以使用-h或–help命令查看帮助菜单
使用示例:
$ ./generator -h
使用后会显示相关提示
$ ./generator -h
usage: generator [-h] [--version] [--build data_dir]
[--expand conf_dir, data_dir conf_dir, data_dir]
[--create data_dir] [--chainca chain_dir]
[--agencyca agency_dir chain_dir agency_name]
[--nodeca node_dir agency_dir node_name] [--gm] [--demo]
[--certbuild cert_dir] [--certexpand cert_dir]
[--deploykey cert_dir, pkg_dir cert_dir, pkg_dir]
[--combine config.ini, config.ini config.ini, config.ini
–combine¶
使用–combine命令可以合并两个config.ini中的p2p section
如 A目录下的config.ini文件的p2p section为
[p2p]
listen_ip = 127.0.0.1
listen_port = 30300
node.0 = 127.0.0.1:30300
node.1 = 127.0.0.1:30301
node.2 = 127.0.0.1:30302
node.3 = 127.0.0.1:30303
B目录下的config.ini文件的p2p section为
[p2p]
listen_ip = 127.0.0.1
listen_port = 30303
node.0 = 127.0.0.1:30300
node.1 = 127.0.0.1:30303
node.2 = 192.167.1.1:30300
node.3 = 192.167.1.1:30301
使用此命令后会成为:
[p2p]
listen_ip = 127.0.0.1
listen_port = 30304
node.0 = 127.0.0.1:30300
node.1 = 127.0.0.1:30301
node.2 = 192.167.1.1:30302
node.3 = 192.167.1.1:30303
node.4 = 192.167.1.1:30300
node.5 = 192.167.1.1:30301
使用示例
$ ./generator --combine ~/mydata/node_A/config.ini ~/mydata/node_B/config.ini
使用成功后会将node_A和node_B的config.ini中p2p section合并与 ~/mydata/node_B/config.ini的文件中
–deploykey¶
使用–deploykey可以将路径下名称相同的节点私钥导入到生成好的安装包中。
使用示例:
$./generator --deploykey ./cert ./data
如./cert下有名为node_127.0.0.1_30300,node_127.0.0.1_30301的文件夹,文件夹中有节点私钥文件node.key
./data下有名为node_127.0.0.1_30300,node_127.0.0.1_30301的安装包
执行完成后可以将./cert下的对应的节点私钥导入./data的安装包中
–gm¶
–gm命令为国密选项,此命令可以与任意命令组合,使用时打开国密开关
使用示例:
生成国密链
./generator --build ~/mydata --gm
扩容国密节点
./generator --expand ./expand ~/mydata --gm
生成国密证书
./genrator --nodeca ./agency_dir node_dir(SET) node_name --gm
监控¶
fisco generator 生成的安装包提供了内置的监控脚本,用户可以通过对其进行配置,将节点的告警信息发送至指定地址。fisco generator会将monitir脚本放置于生成节点安装包的指定目录下,假设用户指定生成的文件夹名为data,则monitor脚本会在data目录下的monitor文件夹下
使用方式如下:
$ cd ./data/monitor
配置告警服务¶
用户使用前,首先需要配置告警信息服务,这里以server酱的微信推送为例,可以参考配置server酱
绑定自己的github账号,以及微信后,可以使用本脚本向微信发送告警信息,使用本脚本的-s命令 可以向指定微信发送告警信息
如果用户希望使用其他服务,可以修改monitor.sh中的alarm() { # change http server}函数,个性化配置为自己需要的服务
help命令¶
使用monitor命令查看脚本使用方式
$ ./monitor.sh -h
Usage : bash monitor.sh
-s : send alert to your address
-m : monitor, statistics. default : monitor .
-f : log file to be analyzed.
-o : dirpath
-p : name of the monitored program , default is fisco-bcos
-g : specified the group list to be analized
-d : log analyze time range. default : 10(min), it should not bigger than max value : 60(min).
-r : setting alert receiver
-h : help.
example :
bash monitor.sh -s YourHttpAddr -o nodes -r your_name
bash monitor.sh -s YourHttpAddr -m statistics -o nodes -r your_name
bash monitor.sh -s YourHttpAddr -m statistics -f node0/log/log_2019021314.log -g 1 2 -r your_name
命令解释如下:
- -s 指定告警配置地址,可以配置为告警上报服务的ip
- -m 设定监控模式,可以配置为statistics和monitor两种模式,默认为monitor模式。
- -f 分析节点log
- -o 指定节点路径
- -p 设定监控上报名称,默认为fisco-bcos
- -g 指定监控群组,默认分析所有群组
- -d log分析时间范围,默认10分钟内的log,最大不超过60分钟
- -r 指定上报接收者名称
- -h 帮助命令
使用示例¶
- 使用脚本监控指定路径下节点,发送给接收者Alice
$ bash monitor.sh -s https://sc.ftqq.com/[SCKEY(登入后可见)].send -o alice/nodes -r Alice
- 使用脚本统计指定路径下节点信息,发送给接收者Alice
$ bash monitor.sh -s https://sc.ftqq.com/[SCKEY(登入后可见)].send -m statistics -o alice/nodes -r Alice
- 使用脚本统计指定路径下节点指定log指定群组1和群组2的信息,发送给接收者Alice
$ bash monitor.sh -s https://sc.ftqq.com/[SCKEY(登入后可见)].send -m statistics -f node0/log/log_2019021314.log -g 1 2 -o alice/nodes -r Alice
场景分析¶
fisco generator针对不同的组网形式进行了分析,以下为不同场景的模式分析和组网示例
场景分析¶
按照一般业务分析,可以将场景分为以下四种,分别是以下四种场景
起始组网¶
ABC三个对等机构需要沟通搭建一条链,开始时只需要一个账本,需要生成安装包,启动节点,初始化一个业务。操作步骤如下:
- A B C三个机构共享自己的节点证书,节点信息(包括hostip,p2pip,端口号等),由某一机构统一收集(或都收集)
- 假设A收集所有资料后,将证书按照cert_p2pip_lport.crt的格式放在meta目录下,并配置mchain.ini中的node信息,指定配置文件中的groupid
- A机构执行build命令,指定生成安装包的目录,会在指定目录下生成多个名为node_p2pip_port.tar.gz如 node_127.0.0.1_30301.tar.gz的安装包(或就是一个文件夹)
- A机构将生成的安装包分发给BC机构,并将自己的安装包推送至对应服务器,拷贝node.key至安装包的/conf文件夹下,启动节点,默认节点加入的群组为mchain.ini的groupid
考虑场景:是否每个机构都收集信息,生成所有包,但是只推送自己的(地位对等),该操作对generator的实现没有影响
新节点加入网络¶
新节点加入网络有两种情况,场景如下:
新节点加入现有group¶
在本场景中,节点需要与所有原有节点进行网络连接,并且需要加入现有群组,以上述起始组网为例,操作步骤如下: 假设D机构需要新加入节点到ABC的组网的group1中(ABC加入节点在网络中同理)
- D机构收集ABC组网生成的config.ini 与 group.1.genesis,group.1.ini文件,统一放置与某一文件夹下,如./node_conf
- D机构配置[mexpand.ini]中的节点信息(包括hostip,p2pip,端口号等),将证书按照cert_p2pip_lport.crt的格式放在meta目录下
- D机构使用expand命令指定./node_conf路径和输出路径,在输出路径生成多个名为node_p2pip_port的文件夹
- D机构将导入私钥,将安装包推送至指定服务器,启动节点
- D机构请求ABC中的某一个机构将自己的节点注册如group1中,完成新节点入网操作
新节点创建新group¶
在本场景中,节点只需要与链中的某几个节点进行网络连接,并且不需要加入原有群组,以上述起始组网为例,假设EF机构需要与C机构组网,新建一个group,对于C机构而言,不需要再从新搭建一条链,直接服用[起始组网]中的节点,新建群组即可满足需求
操作步骤类似于[起始组网]:
- C E F三个机构共享自己的节点证书,节点信息(包括hostip,p2pip,端口号等),由某一机构统一收集(或都收集)
- 假设C收集所有资料后,将证书按照cert_p2pip_lport.crt的格式放在meta目录下,并配置mchain.ini中的node信息,指定配置文件中的groupid(注意,不能与C在group1中的相同)
- C机构执行build命令,指定生成安装包的目录,会在指定目录下生成多个名为node_p2pip_port.tar.gz如 node_127.0.0.1_30301.tar.gz的安装包(或就是一个文件夹)
- C机构将生成的安装包分发给EF机构,EF收到安装包之后,启动节点
- C机构将新生成的group信息如group.2.genesis,group.2.ini文件,放置与原有节点的conf文件夹下,完成新节点创建和新群组建立操作
节点新建群组¶
在本场景中,链中的已有节点需要建立新群组,如何已经生成链接的节点之前没有建立网络链接,则交换config.ini文件,同步网络链接列表
假设节点均已建立为网络链接,先要建立新群组,如上述场景中ABCD要新建立group3,则需要进行的操作如下:
- ABCD中交换节点证书,设A收集完成后,按照cert_p2pip_lport.crt的格式放在meta目录下
- A配置[mgroup.ini]中的信息,(是否要在这里填写groupmember信息)
- A执行create命令,生成group.i.genesis和group.i.ini,将group.i.genesis和group.i.ini交换给BCD
- ABCD将生成group.i.genesis和group.i.ini拷贝到节点的conf/文件夹下,完成新群组的建立
示例分析 – 网状模式组网¶
假设如图所示,联盟链中共有7个节点,链中有三个群组,分别为group1、group2、group3。group1中节点最多,共有5个节点,节点序号为0、1、2、4、5。
group2中有3个节点,节点序号为2、3、6。
group2中有3个节点,节点序号为0、4、6。
组网步骤如下:
构建gourp1¶
- 序号为0、1、2、4、5的节点交换证书,放置于meta文件夹下。
- 修改mchain.ini中的配置项,使其指向对应节点的ip,端口号,指定组id为group1
- 使用build命令,生成安装包
- 拷贝节点私钥到对应节点安装包的conf文件夹下
- 推送节点安装包至对应服务器
构建group2¶
- 序号为0、2、6的节点交换证书,放置于meta文件夹下。
- 修改mchain.ini中的配置项,使其指向对应节点的ip,端口号,指定组id为group2
- 使用build命令,生成安装包
- 序号0、2的节点将生成的group.2.ini和group.2.genesis拷贝至构建group1时生成的节点conf文件夹下,并将新生成节点的config.ini与构建group1时生成的config.ini的section[p2p]中的node.X合并
- 序号6的节点拷贝节点私钥到对应节点安装包的conf文件夹下,并推送节点安装包至对应服务器
构建group3¶
- 序号为0、4、5、6的节点交换证书,放置于meta文件夹下。
- 修改mchain.ini中的配置项,使其指向对应节点的ip,端口号,指定组id为group3
- 使用build命令,生成安装包
- 序号0、4、5、6的节点将生成的group.2.ini和group.2.genesis拷贝至已生成的节点conf文件夹下,并将新生成节点的config.ini与已生成的config.ini的section[p2p]中的node.X合并
至此,完成了如图所示的网状模式组网
示例分析 – 星型模式组网¶
假设如图所示,联盟链中共有9个节点,其中序号为0的节点处于所有的group中,node8为后期扩容的节点
组网步骤如下:
构建gourp1~7¶
- 序号为0的节点和序号为1、2、3、4、5、6、7的节点分别交换证书,放置于meta文件夹下。
- 修改mchain.ini中的配置项,使其指向对应节点的ip,端口号,指定组id为group1~7
- 使用build命令,生成安装包
- 序号0的节点将生成的config.ini合并,并将group的相关配置放置节点conf文件夹下
- 序号为1、2、3、4、5、6、7的节点拷贝节点私钥到对应节点安装包的conf文件夹下
- 推送节点安装包至对应服务器
扩容节点加入group1¶
- 序号为8的节点将自己的证书放置于meta文件夹下。
- 序号为8的节点与序号为0或1的节点交涉,收集fisco-bcos二进制文件,config.ini,group.1.genesis和group.1.ini放置于某路径下,如./data
- 修改mexpand.ini中的配置项,使其指向自己节点的ip,端口号,指定组id为group1
- 使用expand命令,生成安装包
- 拷贝节点私钥到对应节点安装包的conf文件夹下,并推送节点安装包至对应服务器
- 启动节点,等待序号为0或1的节点将自己加入群组
至此,完成了如图所示的星型模式组网
API¶
下列接口的示例中采用curl命令,curl是一个利用url语法在命令行下运行的数据传输工具,通过curl命令发送http post请求,可以访问FISCO BCOS的JSON RPC接口。curl命令的url地址设置为节点配置文件[rpc]
部分的[listen_ip]
和[jsonrpc listen port]
端口。为了格式化json,使用jq工具进行格式化显示。错误返回码参考RPC设计文档。
getClientVersion¶
返回节点的版本信息
参数¶
无
返回值¶
object
- 版本信息,字段如下:Build Time
:string
- 编译时间Build Type
:string
- 编译机器环境FISCO-BCOS Version
:string
- FISCO BCOS版本Git Branch
:string
- 版本分支Git Commit Hash
:string
- 版本最新commit哈希
- 示例
// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"getClientVersion","params":[],"id":1}' http://127.0.0.1:8545 |jq
// Result
{
"id": 83,
"jsonrpc": "2.0",
"result": {
"Build Time": "20190106 20:49:10",
"Build Type": "Linux/g++/RelWithDebInfo",
"FISCO-BCOS Version": "2.0.0",
"Git Branch": "release-2.0.1",
"Git Commit Hash": "693a709ddab39965d9c39da0104836cfb4a72054"
}
}
getBlockNumber¶
返回节点指定群组内的最新区块高度
参数¶
groupID
:unsigned int
- 群组ID
返回值¶
string
- 最新区块高度(0x开头的十六进制字符串)- 示例
// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"getBlockNumber","params":[1],"id":1}' http://127.0.0.1:8545 |jq
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": "0x1"
}
getPbftView¶
返回节点所在指定群组内的最新pbft视图
参数¶
groupID
:unsigned int
- 群组ID
返回值¶
string
- 最新的pbft视图- 示例
// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"getPbftView","params":[1],"id":1}' http://127.0.0.1:8545 |jq
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": "0x1a0"
}
注: FISCO BCOS支持pbft共识和raft共识,当访问的区块链采用raft共识时,该接口返回FISCO BCOS自定义错误响应如下:
{
"error": {
"code": 7,
"data": null,
"message": "Only pbft consensus supports the view property"
},
"id": 1,
"jsonrpc": "2.0"
}
getSealerList¶
返回指定群组内的共识节点列表
参数¶
groupID
:unsigned int
- 群组ID
返回值¶
array
- 共识节点nodeID列表- 示例
// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"getSealerList","params":[1],"id":1}' http://127.0.0.1:8545 |jq
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": [
"037c255c06161711b6234b8c0960a6979ef039374ccc8b723afea2107cba3432dbbc837a714b7da20111f74d5a24e91925c773a72158fa066f586055379a1772",
"0c0bbd25152d40969d3d3cee3431fa28287e07cff2330df3258782d3008b876d146ddab97eab42796495bfbb281591febc2a0069dcc7dfe88c8831801c5b5801",
"622af37b2bd29c60ae8f15d467b67c0a7fe5eb3e5c63fdc27a0ee8066707a25afa3aa0eb5a3b802d3a8e5e26de9d5af33806664554241a3de9385d3b448bcd73"
]
}
getObserverList¶
返回指定群组内的观察节点列表
参数¶
groupID
:unsigned int
- 群组ID
返回值¶
array
- 观察节点nodeID列表- 示例
// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"getObserverList","params":[1],"id":1}' http://127.0.0.1:8545 |jq
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": [
"10b3a2d4b775ec7f3c2c9e8dc97fa52beb8caab9c34d026db9b95a72ac1d1c1ad551c67c2b7fdc34177857eada75836e69016d1f356c676a6e8b15c45fc9bc34"
]
}
getConsensusStatus¶
返回指定群组内的共识状态信息
参数¶
groupID
:unsigned int
- 群组ID
返回值¶
object
- 共识状态信息。- 当共识机制为pbft时(pbft详细设计参考pbft设计文档),字段如下:
accountType
:unsigned int
- 账户类型allowFutureBlocks
:bool
- 允许未来块标志cfgErr
:bool
- 配置错误标志connectedNodes
:unsigned int
- 连接的节点数consensusedBlockNumber
:unsigned int
- 下一个共识的最新块高currentView
:unsigned int
- 当前视图groupId
:unsigned int
- 群组IDhighestblockHash
:string
- 最新块哈希highestblockNumber
:unsigned int
- 最新区块高度leaderFailed
:bool
- leader失败标志max_faulty_leader
:unsigned int
- 最大容错节点数sealer.index
:string
- 节点序号为index的nodeIDnode index
:unsigned int
- 节点的序号nodeID
:string
- 节点的nodeIDnodeNum
:unsigned int
- 节点的数omitEmptyBlock
:bool
- 忽略空块标志位protocolId
:unsigned int
- 协议ID号toView
:unsigned int
- 目前到达的view值prepareCache_blockHash
:string
- prepareCache哈希prepareCache_height
:int
- prepareCache高度prepareCache_idx
:unsigned int
- prepareCache序号prepareCache_view
:unsigned int
- prepareCache视图rawPrepareCache_blockHash
:string
- rawPrepareCache哈希rawPrepareCache_height
:int
- rawPrepareCache高度rawPrepareCache_idx
:unsigned int
- rawPrepareCache序号rawPrepareCache_view
:unsigned int
- rawPrepareCache视图committedPrepareCache_blockHash
:string
- committedPrepareCache哈希committedPrepareCache_height
:int
- committedPrepareCache高度committedPrepareCache_idx
:unsigned int
- committedPrepareCache序号committedPrepareCache_view
:unsigned int
- committedPrepareCache视图futureCache_blockHash
:string
-futureCache哈希futureCache_height
:int
- futureCache高度futureCache_idx
:unsigned int
- futureCache序号signCache_cachedSize
:unsigned int
- signCache_cached大小commitCache_cachedSize
:unsigned int
- commitCache_cached大小viewChangeCache_cachedSize
:unsigned int
- viewChangeCache_cached大小
- 当共识机制为raft时(raft详细设计参考raft设计文档),字段如下:
accountType
:unsigned int
- 账户类型allowFutureBlocks
:bool
- 允许未来块标志cfgErr
:bool
- 配置错误标志consensusedBlockNumber
:unsigned int
- 下一个共识的最新块高groupId
:unsigned int
- 群组IDhighestblockHash
:string
- 最新块哈希highestblockNumber
:unsigned int
- 最新区块高度leaderId
:string
- leader的nodeIDleaderIdx
:unsigned int
- leader的序号max_faulty_leader
:unsigned int
- 最大容错节点数sealer.index
:string
- 节点序号为index的nodeIDnode index
:unsigned int
- 节点的indexnodeID
:string
- 节点的nodeIDnodeNum
:unsigned int
- 节点的数omitEmptyBlock
:bool
- 忽略空块标志位protocolId
:unsigned int
- 协议ID号
示例
// Request pbft
curl -X POST --data '{"jsonrpc":"2.0","method":"getConsensusStatus","params":[1],"id":1}' http://127.0.0.1:8545 |jq
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": [
{
"accountType":1,
"allowFutureBlocks":true,
"cfgErr":false,
"connectedNodes":3,
"consensusedBlockNumber":4,
"currentView":153,
"groupId":1,
"highestblockHash":"98e186095a88f7b1b4cd02e3c405f031950577626dab55b639e024b9f2f8788b",
"highestblockNumber":3,
"leaderFailed":false,
"max_faulty_leader":1,
"sealer.0":"29c34347a190c1ec0c4507c6eed6a5bcd4d7a8f9f54ef26da616e81185c0af11a8cea4eacb74cf6f61820292b24bc5d9e426af24beda06fbd71c217960c0dff0",
"sealer.1":"41285429582cbfe6eed501806391d2825894b3696f801e945176c7eb2379a1ecf03b36b027d72f480e89d15bacd43462d87efd09fb0549e0897f850f9eca82ba",
"sealer.2":"87774114e4a496c68f2482b30d221fa2f7b5278876da72f3d0a75695b81e2591c1939fc0d3fadb15cc359c997bafc9ea6fc37345346acaf40b6042b5831c97e1",
"sealer.3":"d5b3a9782c6aca271c9642aea391415d8b258e3a6d92082e59cc5b813ca123745440792ae0b29f4962df568f8ad58b75fc7cea495684988e26803c9c5198f3f8",
"node index":1,
"nodeID":"41285429582cbfe6eed501806391d2825894b3696f801e945176c7eb2379a1ecf03b36b027d72f480e89d15bacd43462d87efd09fb0549e0897f850f9eca82ba",
"nodeNum":4,
"omitEmptyBlock":true,
"protocolId":264,
"toView":153
},
{
"prepareCache_blockHash":"0000000000000000000000000000000000000000000000000000000000000000",
"prepareCache_height":-1,
"prepareCache_idx":"65535",
"prepareCache_view":"9223372036854775807"
},
{
"rawPrepareCache_blockHash":"0000000000000000000000000000000000000000000000000000000000000000",
"rawPrepareCache_height":-1,
"rawPrepareCache_idx":"65535",
"rawPrepareCache_view":"9223372036854775807"
},
{
"committedPrepareCache_blockHash":"2e4c63cfac7726691d1fe436ec05a7c5751dc4150d724822ff6c36a608bb39f2",
"committedPrepareCache_height":3,
"committedPrepareCache_idx":"2",
"committedPrepareCache_view":"60"
},
{
"futureCache_blockHash":"0000000000000000000000000000000000000000000000000000000000000000",
"futureCache_height":-1,
"futureCache_idx":"65535",
"futureCache_view":"9223372036854775807"
},
{
"signCache_cachedSize":"0"
},
{
"commitCache_cachedSize":"0"
},
{
"viewChangeCache_cachedSize":"0"
}
]
}
// Request raft
curl -X POST --data '{"jsonrpc":"2.0","method":"getConsensusStatus","params":[1],"id":1}' http://127.0.0.1:8545 |jq
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": [
{
"accountType": 1,
"allowFutureBlocks": true,
"cfgErr": false,
"consensusedBlockNumber": 1,
"groupId": 1,
"highestblockHash": "4765a126a9de8d876b87f01119208be507ec28495bef09c1e30a8ab240cf00f2",
"highestblockNumber": 0,
"leaderId": "d5b3a9782c6aca271c9642aea391415d8b258e3a6d92082e59cc5b813ca123745440792ae0b29f4962df568f8ad58b75fc7cea495684988e26803c9c5198f3f8",
"leaderIdx": 3,
"max_faulty_leader": 1,
"sealer.0": "29c34347a190c1ec0c4507c6eed6a5bcd4d7a8f9f54ef26da616e81185c0af11a8cea4eacb74cf6f61820292b24bc5d9e426af24beda06fbd71c217960c0dff0",
"sealer.1": "41285429582cbfe6eed501806391d2825894b3696f801e945176c7eb2379a1ecf03b36b027d72f480e89d15bacd43462d87efd09fb0549e0897f850f9eca82ba",
"sealer.2": "87774114e4a496c68f2482b30d221fa2f7b5278876da72f3d0a75695b81e2591c1939fc0d3fadb15cc359c997bafc9ea6fc37345346acaf40b6042b5831c97e1",
"sealer.3": "d5b3a9782c6aca271c9642aea391415d8b258e3a6d92082e59cc5b813ca123745440792ae0b29f4962df568f8ad58b75fc7cea495684988e26803c9c5198f3f8",
"node index": 1,
"nodeID": "41285429582cbfe6eed501806391d2825894b3696f801e945176c7eb2379a1ecf03b36b027d72f480e89d15bacd43462d87efd09fb0549e0897f850f9eca82ba",
"nodeNum": 4,
"omitEmptyBlock": true,
"protocolId": 267
}
]
}
getSyncStatus¶
返回指定群组内的同步状态信息
参数¶
groupID
:unsigned int
- 群组ID
返回值¶
object
- 同步状态信息,字段如下:blockNumber
:unsigned int
- 最新区块高度genesisHash
:string
- 创世块哈希isSyncing
:bool
- 正在同步标志latestHash
:string
- 最新区块哈希nodeId
:string
- 节点的nodeIDprotocolId
:unsigned int
- 协议ID号txPoolSize
:string
- 交易池中交易的数量peers
:array
- 已连接的指定群组内p2p节点,节点信息字段如下:blockNumber
:unsigned int
- 最新区块高度genesisHash
:string
- 创始区块哈希latestHash
:string
- 最新块哈希nodeId
:string
- 节点的nodeID
- 示例
// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"getSyncStatus","params":[1],"id":1}' http://127.0.0.1:8545 |jq
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"blockNumber": 0,
"genesisHash": "4765a126a9de8d876b87f01119208be507ec28495bef09c1e30a8ab240cf00f2",
"isSyncing": false,
"latestHash": "4765a126a9de8d876b87f01119208be507ec28495bef09c1e30a8ab240cf00f2",
"nodeId": "41285429582cbfe6eed501806391d2825894b3696f801e945176c7eb2379a1ecf03b36b027d72f480e89d15bacd43462d87efd09fb0549e0897f850f9eca82ba",
"peers": [
{
"blockNumber": 0,
"genesisHash": "4765a126a9de8d876b87f01119208be507ec28495bef09c1e30a8ab240cf00f2",
"latestHash": "4765a126a9de8d876b87f01119208be507ec28495bef09c1e30a8ab240cf00f2",
"nodeId": "29c34347a190c1ec0c4507c6eed6a5bcd4d7a8f9f54ef26da616e81185c0af11a8cea4eacb74cf6f61820292b24bc5d9e426af24beda06fbd71c217960c0dff0"
},
{
"blockNumber": 0,
"genesisHash": "4765a126a9de8d876b87f01119208be507ec28495bef09c1e30a8ab240cf00f2",
"latestHash": "4765a126a9de8d876b87f01119208be507ec28495bef09c1e30a8ab240cf00f2",
"nodeId": "87774114e4a496c68f2482b30d221fa2f7b5278876da72f3d0a75695b81e2591c1939fc0d3fadb15cc359c997bafc9ea6fc37345346acaf40b6042b5831c97e1"
},
{
"blockNumber": 0,
"genesisHash": "4765a126a9de8d876b87f01119208be507ec28495bef09c1e30a8ab240cf00f2",
"latestHash": "4765a126a9de8d876b87f01119208be507ec28495bef09c1e30a8ab240cf00f2",
"nodeId": "d5b3a9782c6aca271c9642aea391415d8b258e3a6d92082e59cc5b813ca123745440792ae0b29f4962df568f8ad58b75fc7cea495684988e26803c9c5198f3f8"
}
],
"protocolId": 265,
"txPoolSize": "0"
}
}
getPeers¶
返回已连接的p2p节点信息
参数¶
groupID
:unsigned int
- 群组ID
返回值¶
array
- 已连接的p2p节点信息,字段如下:IPAndPort
:string
- 节点连接的ip和端口NodeID
:string
- 节点的nodeIDTopic
:array
- 节点关注的topic信息
- 示例
// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"getPeers","params":[1],"id":1}' http://127.0.0.1:8545 |jq
// Result
格式化JSON:
{
"id": 1,
"jsonrpc": "2.0",
"result": [
{
"IPAndPort": "127.0.0.1:30308",
"NodeID": "0701cc9f05716690437b78db5b7c9c97c4f8f6dd05794ba4648b42b9267ae07cfcd589447ac36c491e7604242149601d67c415504a838524939ef2230d36ffb8",
"Topic": [ ]
},
{
"IPAndPort": "127.0.0.1:58348",
"NodeID": "353ab5990997956f21b75ff5d2f11ab2c6971391c73585963e96fe2769891c4bc5d8b7c3d0d04f50ad6e04c4445c09e09c38139b1c0a5937a5778998732e34da",
"Topic": [ ]
},
{
"IPAndPort": "127.0.0.1:30300",
"NodeID": "73aebaea2baa9640df416d0e879d6e0a6859a221dad7c2d34d345d5dc1fe9c4cda0ab79a7a3f921dfc9bdea4a49bb37bdb0910c338dadab2d8b8e001186d33bd",
"Topic": [ ]
}
]
}
getGroupPeers¶
返回指定群组内的共识节点和观察节点列表
参数¶
groupID
:unsigned int
- 群组ID
返回值¶
array
- 共识节点和观察节点的nodeID列表- 示例
// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"getGroupPeers","params":[1],"id":1}' http://127.0.0.1:8545 |jq
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": [
"0c0bbd25152d40969d3d3cee3431fa28287e07cff2330df3258782d3008b876d146ddab97eab42796495bfbb281591febc2a0069dcc7dfe88c8831801c5b5801",
"037c255c06161711b6234b8c0960a6979ef039374ccc8b723afea2107cba3432dbbc837a714b7da20111f74d5a24e91925c773a72158fa066f586055379a1772",
"622af37b2bd29c60ae8f15d467b67c0a7fe5eb3e5c63fdc27a0ee8066707a25afa3aa0eb5a3b802d3a8e5e26de9d5af33806664554241a3de9385d3b448bcd73",
"10b3a2d4b775ec7f3c2c9e8dc97fa52beb8caab9c34d026db9b95a72ac1d1c1ad551c67c2b7fdc34177857eada75836e69016d1f356c676a6e8b15c45fc9bc34"
]
}
getNodeIDList¶
返回节点本身和已连接的p2p节点列表
参数¶
groupID
:unsigned int
- 群组ID
返回值¶
array
- 节点本身和已连接p2p节点的nodeID列表- 示例
// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"getNodeIDList","params":[1],"id":1}' http://127.0.0.1:8545 |jq
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": [
"0c0bbd25152d40969d3d3cee3431fa28287e07cff2330df3258782d3008b876d146ddab97eab42796495bfbb281591febc2a0069dcc7dfe88c8831801c5b5801",
"037c255c06161711b6234b8c0960a6979ef039374ccc8b723afea2107cba3432dbbc837a714b7da20111f74d5a24e91925c773a72158fa066f586055379a1772",
"622af37b2bd29c60ae8f15d467b67c0a7fe5eb3e5c63fdc27a0ee8066707a25afa3aa0eb5a3b802d3a8e5e26de9d5af33806664554241a3de9385d3b448bcd73",
"10b3a2d4b775ec7f3c2c9e8dc97fa52beb8caab9c34d026db9b95a72ac1d1c1ad551c67c2b7fdc34177857eada75836e69016d1f356c676a6e8b15c45fc9bc34"
]
}
getGroupList¶
返回节点所属群组的群组ID列表
参数¶
无
返回值¶
array
- 节点所属群组的群组ID列表- 示例
// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"getGroupList","params":[],"id":1}' http://127.0.0.1:8545 |jq
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": [1]
}
getBlockByHash¶
返回根据区块哈希查询的区块信息
参数¶
groupID
:unsigned int
- 群组IDblockHash
:string
- 区块哈希includeTransactions
:bool
- 包含交易标志(true显示交易详细信息,false仅显示交易的hash)
返回值¶
object
- 区块信息,字段如下:extraData
:array
- 附加数据gasLimit
:string
- 区块中允许的gas最大值gasUsed
:string
- 区块中所有交易消耗的gashash
:string
- 区块哈希logsBloom
:string
- log的布隆过滤器值number
:string
- 区块高度parentHash
:string
- 父区块哈希sealer
:string
- 共识节点序号stateRoot
:string
- 状态根哈希timestamp
:string
- 时间戳transactions
:array
- 交易列表,当includeTransactions
为false
时,显示交易的哈希。当includeTransactions
为true
时,显示交易详细信息(详细字段见getTransactionByHash)
- 示例
// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"getBlockByHash","params":[1,"0x910ea44e2a83618c7cc98456678c9984d94977625e224939b24b3c904794b5ec",true],"id":1}' http://127.0.0.1:8545 |jq
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"extraData": [],
"gasLimit": "0x0",
"gasUsed": "0x0",
"hash": "0x910ea44e2a83618c7cc98456678c9984d94977625e224939b24b3c904794b5ec",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"number": "0x1",
"parentHash": "0x4765a126a9de8d876b87f01119208be507ec28495bef09c1e30a8ab240cf00f2",
"sealer": "0x3",
"stateRoot": "0xfb7ca5a7a271c8ffb51bc689b78d0aeded23497c9c22e67dff8b1c7b4ec88a2a",
"timestamp": "0x1687e801d99",
"transactions": [
{
"blockHash": "0x910ea44e2a83618c7cc98456678c9984d94977625e224939b24b3c904794b5ec",
"blockNumber": "0x1",
"from": "0xadf06b974703a1c25c621ce53676826198d4b046",
"gas": "0x1c9c380",
"gasPrice": "0x1",
"hash": "0x022dcb1ad2d940ce7b2131750f7458eb8ace879d129ee5b650b84467cb2184d7",
"input": "0x608060405234801561001057600080fd5b5060016000800160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506402540be40060006001018190555060028060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060006002600101819055506103bf806100c26000396000f30060806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806366c99139146100515780636d4ce63c1461007e575b600080fd5b34801561005d57600080fd5b5061007c600480360381019080803590602001909291905050506100a9565b005b34801561008a57600080fd5b506100936102e1565b6040518082815260200191505060405180910390f35b8060006001015410806100c757506002600101548160026001015401105b156100d1576102de565b8060006001015403600060010181905550806002600101600082825401925050819055507fc77b710b83d1dc3f3fafeccd08a6c469beb873b2f0975b50d1698e46b3ee5b4c816040518082815260200191505060405180910390a160046080604051908101604052806040805190810160405280600881526020017f323031373034313300000000000000000000000000000000000000000000000081525081526020016000800160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600260000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001838152509080600181540180825580915050906001820390600052602060002090600402016000909192909190915060008201518160000190805190602001906102419291906102ee565b5060208201518160010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060408201518160020160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550606082015181600301555050505b50565b6000600260010154905090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061032f57805160ff191683800117855561035d565b8280016001018555821561035d579182015b8281111561035c578251825591602001919060010190610341565b5b50905061036a919061036e565b5090565b61039091905b8082111561038c576000816000905550600101610374565b5090565b905600a165627a7a72305820fb983c66bee66788f407721b23b10a8aae3dc9ef8f1b09e08ec6a6c0b0ec70100029",
"nonce": "0x1a9d06264238ea69c1bca2a74cfced979d6b6a66ce8ad6f5a30e8017b5a98d8",
"to": null,
"transactionIndex": "0x0",
"value": "0x0"
}
],
"transactionsRoot": "0x07506c27626365c4f0db788619a96df1e6f8f62c583f158192700e08c10fec6a"
}
}
// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"getBlockByHash","params":[1,"0x910ea44e2a83618c7cc98456678c9984d94977625e224939b24b3c904794b5ec",false],"id":1}' http://127.0.0.1:8545 |jq
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"extraData": [],
"gasLimit": "0x0",
"gasUsed": "0x0",
"hash": "0x910ea44e2a83618c7cc98456678c9984d94977625e224939b24b3c904794b5ec",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"number": "0x1",
"parentHash": "0x4765a126a9de8d876b87f01119208be507ec28495bef09c1e30a8ab240cf00f2",
"sealer": "0x3",
"stateRoot": "0xfb7ca5a7a271c8ffb51bc689b78d0aeded23497c9c22e67dff8b1c7b4ec88a2a",
"timestamp": "0x1687e801d99",
"transactions": [
"0x022dcb1ad2d940ce7b2131750f7458eb8ace879d129ee5b650b84467cb2184d7"
],
"transactionsRoot": "0x07506c27626365c4f0db788619a96df1e6f8f62c583f158192700e08c10fec6a"
}
}
getBlockByNumber¶
返回根据区块高度查询的区块信息
参数¶
groupID
:unsigned int
- 群组IDblockNumber
:string
- 区块高度(0x开头的十六进制字符串)includeTransactions
:bool
- 包含交易标志(true显示交易详细信息,false仅显示交易的hash)
返回值¶
- 示例
// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"getBlockByNumber","params":[1,"0x0",true],"id":1}' http://127.0.0.1:8545 |jq
Result见getBlockByHash
getBlockHashByNumber¶
返回根据区块高度查询的区块哈希
参数¶
groupID
:unsigned int
- 群组IDblockNumber
:string
- 区块高度(0x开头的十六进制字符串)
返回值¶
blockHash
:string
- 区块哈希- 示例
// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"getBlockHashByNumber","params":[1,"0x1"],"id":1}' http://127.0.0.1:8545 |jq
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": "0x10bfdc1e97901ed22cc18a126d3ebb8125717c2438f61d84602f997959c631fa"
}
getTransactionByHash¶
返回根据交易哈希查询的交易信息
参数¶
groupID
:unsigned int
- 群组IDtransactionHash
:string
- 交易哈希
返回值¶
object
: - 交易信息,其字段如下:blockHash
:string
- 包含该交易的区块哈希blockNumber
:string
- 包含该交易的区块哈希from
:string
- 发送者的地址gas
:string
- 发送者提供的gasgasPrice
:string
- 发送者提供的gas的价格hash
:string
- 交易哈希input
:string
- 交易的输入nonce
:string
- 交易的nonce值to
:string
- 接收者的地址,创建合约交易的该值为nulltransactionIndex
:string
- 交易的序号value
:string
- 转移的值
- 示例
// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"getTransactionByHash","params":[1,"0x7536cf1286b5ce6c110cd4fea5c891467884240c9af366d678eb4191e1c31c6f"],"id":1}' http://127.0.0.1:8545 |jq
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"blockHash": "0x10bfdc1e97901ed22cc18a126d3ebb8125717c2438f61d84602f997959c631fa",
"blockNumber": "0x1",
"from": "0x6bc952a2e4db9c0c86a368d83e9df0c6ab481102",
"gas": "0x9184e729fff",
"gasPrice": "0x174876e7ff",
"hash": "0x7536cf1286b5ce6c110cd4fea5c891467884240c9af366d678eb4191e1c31c6f",
"input": "0x48f85bce000000000000000000000000000000000000000000000000000000000000001bf5bd8a9e7ba8b936ea704292ff4aaa5797bf671fdc8526dcd159f23c1f5a05f44e9fa862834dc7cb4541558f2b4961dc39eaaf0af7f7395028658d0e01b86a37",
"nonce": "0x65f0d06e39dc3c08e32ac10a5070858962bc6c0f5760baca823f2d5582d03f",
"to": "0xd6f1a71052366dbae2f7ab2d5d5845e77965cf0d",
"transactionIndex": "0x0",
"value": "0x0"
}
}
getTransactionByBlockHashAndIndex¶
返回根据区块哈希和交易序号查询的交易信息
参数¶
groupID
:unsigned int
- 群组IDblockHash
:string
- 区块哈希transactionIndex
:string
- 交易序号
返回值¶
- 示例
// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"getTransactionByBlockHashAndIndex","params":[1,"0x10bfdc1e97901ed22cc18a126d3ebb8125717c2438f61d84602f997959c631fa","0x0"],"id":1}' http://127.0.0.1:8545 |jq
Result见getTransactionByHash
getTransactionByBlockNumberAndIndex¶
返回根据区块高度和交易序号查询的交易信息
参数¶
groupID
:unsigned int
- 群组IDblockNumber
:string
- 区块高度(0x开头的十六进制字符串)transactionIndex
:string
- 交易序号
返回值¶
- 示例
// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"getTransactionByBlockNumberAndIndex","params":[1,"0x1","0x0"],"id":1}' http://127.0.0.1:8545 |jq
}
Result见getTransactionByHash
getTransactionReceipt¶
返回根据交易哈希查询的交易回执信息
参数¶
groupID
:unsigned int
- 群组IDtransactionHash
:string
- 交易哈希
返回值¶
object
: - 交易信息,其字段如下:blockHash
:string
- 包含该交易的区块哈希blockNumber
:string
- 包含该交易的区块哈希contractAddress
:string
- 合约地址,如果创建合约,则为”0x0000000000000000000000000000000000000000”from
:string
- 发送者的地址gasUsed
:string
- 交易消耗的gaslogs
:array
- 交易产生的loglogsBloom
:string
- log的布隆过滤器值status
:string
- 交易的状态值to
:string
- 接收者的地址,创建合约交易的该值为nulltransactionHash
:string
- 交易哈希transactionIndex
:string
- 交易序号
- 示例
// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"getTransactionReceipt","params":[1,"0x7536cf1286b5ce6c110cd4fea5c891467884240c9af366d678eb4191e1c31c6f"],"id":1}' http://127.0.0.1:8545 |jq
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"blockHash": "0x10bfdc1e97901ed22cc18a126d3ebb8125717c2438f61d84602f997959c631fa",
"blockNumber": "0x1",
"contractAddress": "0x0000000000000000000000000000000000000000",
"from": "0x6bc952a2e4db9c0c86a368d83e9df0c6ab481102",
"gasUsed": "0x64d8",
"logs": [ ],
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"status": "0x0",
"to": "0xd6f1a71052366dbae2f7ab2d5d5845e77965cf0d",
"transactionHash": "0x7536cf1286b5ce6c110cd4fea5c891467884240c9af366d678eb4191e1c31c6f",
"transactionIndex": "0x0"
}
}
getPendingTransactions¶
返回待打包的交易信息
参数¶
groupID
:unsigned int
- 群组ID
返回值¶
object
: - 带打包的交易信息,其字段如下:blockHash
:string
- 包含该交易的区块哈希blockNumber
:string
- 包含该交易的区块哈希contractAddress
:string
- 合约地址,如果创建合约,则为”0x0000000000000000000000000000000000000000”from
:string
- 发送者的地址gas
:string
- 发送者提供的gasgasPrice
:string
- 发送者提供的gas的价格hash
:string
- 交易哈希input
:string
- 交易的输入nonce
:string
- 交易的nonce值to
:string
- 接收者的地址,创建合约交易的该值为nullvalue
:string
- 转移的值
- 示例
// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"getPendingTransactions","params":[1],"id":1}' http://127.0.0.1:8545 |jq
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": {
[
{
"from": "0x6bc952a2e4db9c0c86a368d83e9df0c6ab481102",
"gas": "0x9184e729fff",
"gasPrice": "0x174876e7ff",
"hash": "0x7536cf1286b5ce6c110cd4fea5c891467884240c9af366d678eb4191e1c31c6f",
"input": "0x48f85bce000000000000000000000000000000000000000000000000000000000000001bf5bd8a9e7ba8b936ea704292ff4aaa5797bf671fdc8526dcd159f23c1f5a05f44e9fa862834dc7cb4541558f2b4961dc39eaaf0af7f7395028658d0e01b86a37",
"nonce": "0x65f0d06e39dc3c08e32ac10a5070858962bc6c0f5760baca823f2d5582d03f",
"to": "0xd6f1a71052366dbae2f7ab2d5d5845e77965cf0d",
"value": "0x0"
}
]
}
}
getPendingTxSize¶
返回待打包的交易数量
参数¶
groupID
:unsigned int
- 群组ID
返回值¶
string
: - 待打包的交易数量- 示例
// Request
curl -X POST --data '{"jsonrpc":"2.0","method":""getPendingTxSize","params":[1],"id":1}' http://127.0.0.1:8545 |jq
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": "0x1"
}
getCode¶
返回根据合约地址查询的合约数据
参数¶
groupID
:unsigned int
- 群组IDaddress
:string
- 合约地址
返回值¶
string
: - 合约数据- 示例
// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"getCode","params":[1,"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b"],"id":1}' http://127.0.0.1:8545 |jq
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": "0x60606040523415600b57fe5b5b60928061001a6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680636d4ce63c14603a575bfe5b3415604157fe5b6047605d565b6040518082815260200191505060405180910390f35b60004290505b905600a165627a7a723058203d9c292921247163d180a161baa8db840c9da6764cab1d23f1e11a5cff13c7910029"
}
getTotalTransactionCount¶
返回当前交易总数和区块高度
参数¶
groupID
:unsigned int
- 群组ID
返回值¶
object
: - 当前交易总数和区块高度信息,其字段如下:txSum
:string
- 交易总数blockNumber
:string
- 区块高度
- 示例
// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"getTotalTransactionCount","params":[1],"id":1}' http://127.0.0.1:8545 |jq
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"txSum": "0x1",
"blockNumber": "0x1"
}
}
getSystemConfigByKey¶
返回根据key值查询的value值
参数¶
groupID
:unsigned int
- 群组IDkey
:string
- 支持tx_count_limit和tx_gas_limit
返回值¶
string
- value值- 示例
// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"getSystemConfigByKey","params":[1,"tx_count_limit"],"id":1}' http://127.0.0.1:8545 |jq
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": "1000"
}
call¶
执行一个可以立即获得结果的请求,无需区块链共识
参数¶
groupID
:unsigned int
- 群组IDobject
: - 请求信息,其字段如下:from
:string
- 发送者的地址to
:string
- 接收者的地址value
:string
- (可选)转移的值data
:string
- (可选)编码的参数,编码规范参考Ethereum Contract ABI
返回值¶
string
- 执行的结果- 示例
// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"call","params":[1,{"from":"0x6bc952a2e4db9c0c86a368d83e9df0c6ab481102","to":"0xd6f1a71052366dbae2f7ab2d5d5845e77965cf0d","value":"0x1","data":"0x3"}],"id":1}' http://127.0.0.1:8545 |jq
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": {
"currentBlockNumber": "0x1",
"output": "0x"
}
}
sendRawTransaction¶
执行一个签名的交易,需要区块链共识
参数¶
groupID
:unsigned int
- 群组IDrlp
:string
- 签名的交易数据
返回值¶
string
- 交易哈希- 示例
// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"sendRawTransaction","params":[1,"f8ef9f65f0d06e39dc3c08e32ac10a5070858962bc6c0f5760baca823f2d5582d03f85174876e7ff8609184e729fff82020394d6f1a71052366dbae2f7ab2d5d5845e77965cf0d80b86448f85bce000000000000000000000000000000000000000000000000000000000000001bf5bd8a9e7ba8b936ea704292ff4aaa5797bf671fdc8526dcd159f23c1f5a05f44e9fa862834dc7cb4541558f2b4961dc39eaaf0af7f7395028658d0e01b86a371ca00b2b3fabd8598fefdda4efdb54f626367fc68e1735a8047f0f1c4f840255ca1ea0512500bc29f4cfe18ee1c88683006d73e56c934100b8abf4d2334560e1d2f75e"],"id":1}' http://127.0.0.1:8545 |jq
// Result
{
"id": 1,
"jsonrpc": "2.0",
"result": "0x7536cf1286b5ce6c110cd4fea5c891467884240c9af366d678eb4191e1c31c6f"
}
// FISCO BCOS支持国密算法,采用国密算法的区块链请求示例
// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"sendRawTransaction","params":[1,"f8ef9f65f0d06e39dc3c08e32ac10a5070858962bc6c0f5760baca823f2d5582d03f85174876e7ff8609184e729fff82020394d6f1a71052366dbae2f7ab2d5d5845e77965cf0d80b86448f85bce000000000000000000000000000000000000000000000000000000000000001bf5bd8a9e7ba8b936ea704292ff4aaa5797bf671fdc8526dcd159f23c1f5a05f44e9fa862834dc7cb4541558f2b4961dc39eaaf0af7f7395028658d0e01b86a371ca00b2b3fabd8598fefdda4efdb54f626367fc68e1735a8047f0f1c4f840255ca1ea0512500bc29f4cfe18ee1c88683006d73e56c934100b8abf4d2334560e1d2f75e"],"id":1}' http://127.0.0.1:8545 |jq
FAQ¶
版本相关¶
问:
FISCO BCOS v2.0与之前版本有哪些变化?
答:
请 参考这里。
问:
FISCO BCOS v2.0都有哪些API?开发者如何与FISCO BCOS平台交互?
答:
FISCO BCOS的API为
- FISCO BCOS v2.0提供jsonrpc接口,具体请 参考这里。
- FISCO BCOS v2.0提供Java SDK帮助开发者快速实现应用,具体请 参考这里。
- FISCO BCOS v2.0提供控制台帮助用户快速了解使用FISCO BCOS,具体请 参考这里。
问:
FISCO BCOS v2.0如何搭建?
答:
FISCO BCOS支持多种搭建方式,常用方式有:
问:
FISCO BCOS v2.0的智能合约与之前版本合约有什么不同,兼容性如何?
答:
FISCO BCOS v2.0支持最新的Solidity合约,同时增加了precompile合约,具体请 参考这里。
问:
国密和普通版本的区别有哪些?
答:
国密版FISCO BCOS将交易签名验签、p2p网络连接、节点连接、数据落盘加密等底层模块的密码学算法均替换为国密算法。同时在编译版本,证书,落盘加密,solidity编译java,web3sdk使用国密版本和普通版本都有区别,具体请 参考这里。
问:
是否支持从1.3或1.5升级到2.0版本?
答:
不支持。
控制台¶
问:
控制台指令区分大小写吗?
答:
只支持小写。
问:
加入共识列表或观察者列表报错,nodeID is not in network,为什么?
答:
节点加入共识列表和观察者列表的节点必须是连接peer的nodeID列表里面的成员。
问:
删除节点操作报错,nodeID is not in group peers,为什么?
答:
节点删除操作中的节点必须是getGroupPeers里面展示的group的peers。
问:
游离节点(非群组节点)是否可以同步group数据?
答:
游离节点不参与group内的共识、同步和出块,游离节点可以通过am/ao命令可以将退出的节点添加为共识/观察节点。
问:
某节点属于不同的group,是否可以支持查询多group的信息。
答:
可以,在进入控制台时,输入要查看的groupID: ./start [groupID]
FISCO BCOS使用¶
问:
系统配置、群组配置、节点配置分别指什么?
答:
系统配置是指节点配置中一些影响账本功能,并需账本节点共识的配置项。群组配置指节点所属的群组的相关配置,节点的每个群组都有独立的配置。节点配置指所有可配置项。
问:
群组配置都是可改的吗?
答:
从配置项是否可改的维度,分为
- 节点首次启动生成创世块后不能再修改。这类配置放置于group.x.genesis文件,其中x表示组编号,全链唯一。
- 通过发交易修改配置项实现账本内一致。
- 修改自身配置文件后,节点重启生效。这类配置放置于
group.x.ini
文件。群组配置改后重启可改项就是本地配置,nodeX/conf下的group.*.ini
文件,更改重启生效。涉及配置项为[tx_pool].limit(交易池容量),[consensus].ttl(节点转发数)。
问:
群组配置用户可以改的涉及哪些配置?
答:
群组可修改配置分为共识可改配置和手工可改配置
- 共识可改配置:全组所有节点相同,共识后生效。[consensus].max_trans_num,[consensus].node.X,[tx].gas_limit。
- 手工可改配置:
group.x.ini
文件中,修改后重启生效,只影响节点。配置项有[tx_pool].limit。
问:
群组共识可改配置如何更改、查询?
答:
共识可改配置可以通过控制台修改。共识可改配置项查询除了控制台外,还可以通过RPC接口查询,具体请 参考这里。
- [consensus].max_trans_num,[tx].gas_limit使用接口setSystemConfigByKey(ssc)更改,对于的配置项为tx_count_limit,tx_gas_limit。具体参见ssc -h 。
- [consensus].node.X的更改涉及到节点管理,控制台接口涉及到addSealer(as),addObserver(ao),removeNode(rn),具体参考《节点管理》。
问:
群组观察节点和共识节点有什么区别?
答:
观察节点能同步群组数据,但不能参与共识。共识节点除了具有观察者权限,还参与共识。
问:
如何将合约纳入CNS管理?
答:
在部署合约时,调用CNS合约接口,将合约name、version、address信息写入CNS表中
问:
如何查询合约CNS表?
答:
通过web3sdk控制台指令查询,查询指令根据合约name查询。
Java SDK¶
问:
Java SDK对Java版本有要求吗?
答:
推荐使用oracle jdk 1.8(open-jdk 1.8在某些操作系统上会缺少椭圆曲线的包。)
问:
Java SDK配置完成,发送交易失败的原因是什么?
答:
applicationContext.xml中的ip、端口、群组号填错或者是缺少节点的ca.crt、node.crt和node.key文件。
企业工具¶
问: 企业工具使用时出现找不到pip
答: 企业工具依赖python pip,使用以下命令安装
$ python -m pip install
问: 企业工具使用时出现
Traceback (most recent call last):
File "./generator", line 19, in <module>
from pys.build import config
File "/data/asherli/generator/pys/build/config.py", line 25, in <module>
import configparse
答: 系统缺少python configparser模块,请按照以下命令安装
$ pip install configparser