1.服务器资源及网络(试点机构自备)
卫生健康基础资源链既是一个安全可信的分布式数据管理和计算服务平台,又是一个基于P2P的服务网络。理论上讲,每个节点之间都可以直接互联访问。
本次试点建设的目标:基于开源的基础区块链平台,搭建四川省卫生健康基础资源共享链;多方参与,构建基础资源共享链服务网络;多方协同,制定统一标准的合约开发和管理服务标准,引导厂商实现各自区块链平台的标准服务,为跨链共享和协同提供基础。
基本的区块链网络拓扑如下:

区块链节点网络拓扑图
1.1区块链服务器配置
1.1.1应用系统简介
- 共识节点:全账本记账,参与数据上链的共识和数据验证,共识节点分布越广,公信力越强。建议参与者为各卫生健康信息中心,每个单位管理至少1个节点;
- 验证节点:全账本记账,可参与数据验证,但不参与共识。建议参与者为各医院,每家管理1个节点。
- BAAS平台:BaaS 是一种帮助用户创建、管理和维护区块链网络及应用的服务平台。它具有降低开发及使用成本,兼顾快速部署、方便易用、高安全可靠等特性,是为区块链应用开发者提供区块链服务能力的平台。
- 区块链浏览器:区块链平台通过区块链浏览器实现链上数据的可视化,区块链浏览器分为前端网站和后端数据服务以及依赖的区块链节点。用户通过区块链浏览器查询区块链相关的各种数据,数据来源于后端数据服务以及依赖的区块链节点。
- 存证服务:存证服务属于连接上层应用系统和底层区块链系统的中间层服务,通过标准接口,可以实现应用层数据的便捷上链存证,超时自动重传,数据展开查询等能力。
1.1.2区块链共识网络(省、市级中心、各级医院相关节点)
序号 | 部署机构 | 服务器用途 | 数量 | 配置建议 | 开放端口 | 备注 |
1 | 省卫生健康信息中心 | 共识节点 | ≥1台 | 操作系统:CentOS(7.6 64位),或Ubuntu 18以上;CPU:8核; 内存:16G; 硬盘:1T。
| 172.18.87.232端口: 10200(P2P通信)10201(共识通信)10202(jsonrpc服务) 10203(grpc服务) 172.18.81.126端口: 10108(SSH)
| 省、市级卫生健康信息中心及部分大型医院,组建区块链分布式共识网络,基于3F+1的原则,至少需要4个共识节点;前期若市级单位参与数量不足3家,可由省级单位补足4个节点,后期随着更多市级单位的加入,共识节点可增加。 |
2 | 各级卫生健康信息中心及部分大型医院 | 共识节点 | ≥1台 | 10200(P2P通信)10201(共识通信)10202(jsonrpc服务) 10203(grpc服务) 10108(SSH) | ||
3 | 各试点机构、医院 | 验证节点 | 各1台 | 省、市各级医院、机构,以验证节点身份加入区块链网络,每家医院1个节点。 |
1.1.3区块链服务管理系统(省级中心统一部署管理)
序号 | 部署机构 | 服务器用途 | 数量 | 配置建议 | 开放端口 | 备注 |
1 | 省卫生健康信息中心
| BAAS平台 | 1台 | 操作系统:CentOS(7.6 64位),或Ubuntu 18以上;CPU:8核; 内存:16G; 硬盘:500G。 | 172.18.81.126端口:10103(HTTP) 10104(HTTPS) 10105(指标上报端口) 10106(日志上报端口) 10107(文件服务端口) | 由省卫生健康信息中心统一运营管理,包括监控、运维区块链节点,平行链扩建审核,合约应用管理等。 |
2 | 区块链浏览服务 | 1台 | CPU:4核; 内存:8G; 硬盘:200G SSD; 带宽:100M | 172.18.87.232端口: 10206(链上查询) | 前期由省卫生健康信息中心统一运营管理,后期其他单位也可以独立部署。 | |
3 | 存证服务-系统后台 | 1台 | 操作系统:CentOS(7.6 64位),或Ubuntu 18以上;CPU:8核;内存:16G; 硬盘:1T。 | 172.18.87.232端口:10204(存证) 10205(查询) | 包装区块链接口,提供便捷上链服务。前期由省卫生健康信息中心统一运营管理,后期其他单位也可以独立部署。 | |
4 | 存证服务-展开服务 | 1台 | CPU:8核;内存:16G; 硬盘:500G。 | 提供链上数据查询服务 | ||
5 | 存证服务-数据库 | 1台 | CPU:4核;内存:8G; 硬盘:500G SSD:宽带:20M;MySql 5.7以上 | 存证数据库,方便数据统计、分析 |
1.2项目实施计划
1.2.1项目组组成
项目总协调:林晓东,13981924470。
试点机构协调联系人:(请各自完善)
- 华西医院:
- 华西二院:
- 省人民医院:
- 省妇幼保健院:
- 成都市第三人民医院:
- 成都市卫生健康信息中心:
- 南昌市卫生健康信息化中心:
- 攀枝花卫生健康信息化中心:
- 温江区卫建局:
……
项目技术支持厂商:
- 实施技术协调经理:余芳霞17706425022
- 技术支持工程师:斯崧倍:19818253373(基础区块链平台建设部署)
1.2.2实施计划
时间 | 实施地点 | 工作内容 | 参与人员 | 现场配合 | 备注 |
2022-03月04日 | 根据现场情况确定 | 部署准备工作 | 杨伟、陈均、景帅 |
|
|
2022年03月07日—9日 | 华西医院 | 区块链底层部署 | 杨伟、陈均、景帅 | 华西医院信息中心 | 服务器及网络正常 |
2022年03月10日—14日 | 华西二院 | 区块链底层部署 | 杨伟、陈均、景帅 | 华西二院信息中心 | 服务器及网络正常 |
2022年03月15日—17日 | 省人民医院 | 区块链底层部署 | 杨伟、陈均、景帅 | 省人民医院信息中心 | 服务器及网络正常 |
2022年03月15日—17日 | 省妇幼保健院 | 区块链底层部署 | 杨伟、陈均、景帅 | 省妇幼保健院信息中心 | 服务器及网络正常 |
2022年03月18日—22日 | 成都市第三人民医院 | 区块链底层部署 | 杨伟、陈均、景帅 | 成都市第三人民医院信息中心 | 服务器及网络正常 |
2022年03月23日—25日 | 成都市卫生健康信息中心 | 区块链底层部署 | 杨伟、陈均、景帅 | 成都市卫生健康信息中心 | 服务器及网络正常 |
2022年03月26日—28日 | 南昌市卫生健康信息化中心 | 区块链底层部署 | 杨伟、陈均、景帅 | 南昌市卫生健康信息化中心 | 服务器及网络正常 |
2022年03月28日—30日 | 攀枝花卫生健康信息化中心 | 区块链底层部署 | 杨伟、陈均、景帅 | 攀枝花卫生健康信息化中心 | 服务器及网络正常 |
2022年03月31日—04月03日 | 温江区卫建局 | 区块链底层部署 | 杨伟、陈均、景帅 | 温江区卫建局信息中心 | 服务器及网络正常 |
2.技术架构与部署流程
2.1基础设计模型

BAAS平台:用于快速搭建、管理区块链
联盟链:区块链底层
区块链浏览器:展示区块链数据
存证服务:提供存证上链接口服务
存证查询:查询存证数据,起到溯源作用
应用层:电子证照等应用系统
联盟链支持主链+平行链的架构,第一阶段建设中只部署主链,平行链用于后期企业敏感信息加密上链、数据付费共享等场景。
主链分两种节点:共识节点与验证节点。
共识节点:参与共识,不同单位的加入可增强链的公信力。建议参与者为省卫生健康信息中心、市州级卫生健康信息中心、市区县级卫生健康信息中心。节点数影响拜占庭容错共识机制(3f+1),共识节点至少4台起步;
验证节点:全账本记账,可参与数据验证,但不参与共识。建议参与者为市州级医院、区市县级等单位,每个单位各一个节点。
2.2部署流程

部署流程图
2.2.1联盟链部署
卫健委等单位开通VPN,参与团队进行远程部署支持。
2.2.1.1 BAAS部署
首先部署BAAS平台。针对区块链部署繁琐、运维成本高、应用研发门槛高等痛点,BAAS平台提供一键部署、可视化监控运维、应用研发组件支持等一站式在线服务,帮助用户更专注于核心业务的研发和创新,实现业务快速上链,为区块链生态提供了全流程场景搭建和技术赋能。
2.2.2.2 区块链部署
通过BAAS平台部署对应的区块链。
2.2.2.3 区块链浏览器部署
区块链浏览器用于展示区块链数据。
2.2.2应用数据上链
卫健委等单位开通VPN,参与团队进行远程部署支持。
2.2.2.1 存证服务部署
存证服务平台自身提供上链功能,也为第三方应用提供上链接口。
2.2.2.2 展开服务部署
展开服务提供存证数据查询功能。
2.2.2.3 应用数据上链
应用数据通过存证服务或SDK上链。存证服务数据上链流程如下图所示:

- 应用系统只需要将要上链的数据对象构造成json的格式,传给存证服务,同时系统也会从响应中拿到一个交易的hash。
- 在之后有关改数据对象的增量存证操作过程中,以这个hash值为数据接口贯穿全流程,这个hash就是这份数据对象生命周期的唯一标识。
- 通过溯源校验客户端,用户可以通过元数据中的某些元素,或者存证的hash,溯源校验整个上链对象的生命周期。
数据上链原理如下图所示:

3.业务系统与区块链集成接口标准方案
3.1 设计标准原则
为了保证区块链平台和各集成系统间高效、准确地交换信息,接口的建设应符合如下原则:
1、区块链平台应具备完整性、规范性、开放性和灵活性;
2、接口定义应遵循易理解、易使用、易交流和易扩展的原则;
3、在满足业务需求的前提下,使接口数据量最少,最大限度地减轻数据提供方压力;
3.2 集成内容
本方案规定的集成范围为各个业务系统,其中要集成的数据包括但不限的数据存证,资产数字化等。
3.3 接口要求
区块链平台提供上链集成接口,综合考虑以下内容,满足各业务系统上链需求:
从各业务系统上链数据能够被更好的高效、利用(查询和追溯)上考虑,上链数据需直观的标识数据属性信息如(数据来源,数据关键内容);
从数据安全性上考虑,在业务数据中涉密的信息应脱敏上传,加密上传或不上传;
从存储方面考虑, 大文件(文本,图片,音频,视频,压缩包等)应该hash后再上传区块链,文件本身还是在文件服务器中存储。
业务系统需按照json格式将需要上链的业务数据进行灵活处理,可以是只有一个json对象,也可以组装成json对象嵌套json对象。以产品溯源为例:封装格式说明如下:

3.4业务系统接口详细说明
应用系统通过存证服务接口或JAVA-SDK实现同区块链之间的交互,实现数据上链,链上数据查询等能力。
存证服务通过JSON RPC接口实现应用层数据上链和数据查询服务;同时提供缓存和重传功能;应用系统只需要将数据发送给存证服务,上链是否能成功,由存证服务来保证,降低应用系统的改造量,存证服务主要协助应用层实现数据的上链存证。
JAVA-SDK 提供区块链公私钥生成的方法,交易签名方法,存证合约调用方法,以及区块链的查询方法。每一笔数据上链都实时返回对应的hash, 应用层可以将hash存储起来,前后数据的关联和溯源可以通过应用层存储的hash实现。 JAVA-SDK的功能相对灵活,除了可以调用存证合约实现资产数字化,也可以调用积分合约实现资产数字化,资产转移,增发等能力。
3.4.1存证服务接口集成方案
3.4.1.1 业务数据上链
在业务的各关键步骤,调用区块链的上链接口,将业务数据组装成json串上链,数据上链后,区块链会实时的返回【上链数据hash值】,这串hash值将作为这次上链的唯一值,需要在应用系统中和业务数据绑定。
3.4.1.2 数据查询
调用区块链的查询接口,实现上链的业务数据的查询
3.4.2 存证服务相关接口说明及样例
3.4.2.1 数据上链
调用方式:JSON API调用
http请求方式: POST
接口URL:http://127.0.0.1:46789/api/auto_proofs
请求参数
请求入参类型为数组,支持多个数据同时上传,数组内的对象如下所示:
参数 | 类型 | 是否必填 | 说明 |
detail | string | 是 | 将元数据填入即可 |
id | String | 是 | 由于支持多条数据同时上链,id用于识别匹配各条数据 |
curl -H "Content-type: application/json" -X POST -d '[{\"产品说明\": {\"产品编号\": \"PRODUCT00001\", \"产品说明\": \"产品的具体描述\"}, \"生产阶段\": {\"生产日期\": \"20200421\", \"生产环境\": \"温度,湿度等信息\"},\"质检阶段\": {\"质检人员\": \"张三\", \"质检时间\": \"20200501\",\"质检结果\": \"合格\",\"质检报告\": \"报告文件的hash值\"}}]' http://127.0.0.1:46789/api/auto_proofs
响应参数
序号 | 名称 | 编码 | 类型 | 可空 | 父节点 | 备注 |
1 | 响应码 | code | Varchar(50) | N | ||
2 | 响应消息 | message | Varchar(200) | N | ||
3 | 响应数据 | result | JSON | N | ||
4 | 上链失败项目 | fail | Array | Y | data | |
主键 | id | Varchar(200) | Y | fail | ||
错误原因 | err | Varchar(200) | Y | fail | ||
5 | 上链成功项目 | suc | Array | Y | data | |
主键 | id | Varchar(200) | Y | suc | ||
自动存证hash | tx_hash | varchar(200) | Y | history | ||
错误原因 | err | Varchar(200) | Y | suc |
响应参数样例
{
"code": 200,
"result": {
"fail": [{
"id": "string",
"err": "string"
}],
"suc": [{
"id": "string",
"tx_hash": "string"
}]
},
"message": "OK"
}
3.4.2.2 查上链源数据
调用方式: JSON API调用
http请求方式: POST
接口URL:http://127.0.0.1:8801/
请求报文:
curl -H "Content-type: application/json" -X POST -d '{"method":"Chain33.QueryTransaction","params":[{"hash":"0x8c9dffa9d86807d79636860a0ad9ea81283f37d95db6be9c930f90eb1993b967"}]}' http://127.0.0.1:8801
参数说明:
参数 | 类型 | 是否必填 | 说明 |
hash | String | 是 | 交易哈希 |
响应报文:
{
"id": int32,
"result": {
"tx": {
"execer": "string",
"payload": "string",
"rawPayload": "string",
"fee": int64,
"expire": int32,
"nonce": int32,
"to": "string",
"signature": {
"ty": int32,
"pubkey": "string",
"signature": "string"
}
},
"receipt": {
"ty": int32,
"logs": [{
"ty": int32,
"log": "string"
}]
},
"proofs": ["string"],
"height": int64,
"index": int32,
"blockTime": int64,
"amount": int64,
"fromAddr": "string"
"actionName": "string"
}
}
参数说明:
参数 | 类型 | 说明 |
---|---|---|
result.tx | json | 交易基本信息 |
result.receipt | json | 交易执行结果信息 |
result.actionName | string | 操作名称,不同的执行器可能会有不同的值,如coins(transfer,withdraw,genesis),ticket(genesis,open,close,miner) |
result.receipt.ty | int32 | receipt.ty == 1 表示执行失败;receipt.ty == 2 表示执行成功 |
样例:
curl -H "Content-type: application/json" -X POST -d '{"jsonrpc":"2.0","id":1,"method":"Chain33.QueryTransaction","params":[{"hash":"0x41b54128ad8a2c5636c15cd1a4cc7fb74a223871a3c9529a3cd99c9e20f840f7"}]}' http://127.0.0.1:8801/
返回结果取 tx.rawPayload字段,比如数据为:
0x7b2264617461223a224948736935597936355a3258365a4f2b35622b463661473735714348364b2b47496a6f6757337369354c7942354c696135714348364b2b47496a6f67496c704852456f694c434c6b754a726c6971486d6f49666f723459694f69416935355366354c716e35374737354c696135597168496977693570326c3572715135374f3735377566496a6f67496b394249697769357061483571476a35704759364b6142496a6f67496a42344f544d324f446c684e7a413159574d77596d49304e6a45794f4449304f44677a4d4459775a44637a5a4441794e544d305a6a6869595463314f475931593245794d57457a4e444e695a5746694d6d4a6d4e3249304e79497349756167682b65747669493649434c6d6f61506d6f596a6e7362736966563073497553346d75574b6f656167682b69766869493649467437497553346d75574b6f65654f722b694b6769493649434c6b754a506f67597a6d6f61506d6f596a6c6b5a6a6c765a4c6d6f614d694c434c6b754a726c6971486f7634666e714973694f6941693570573035354347356232533571476a496e316466513d3d222c2276657273696f6e223a2256322e302e30222c226f7074696f6e223a225733736961325635496a6f696447567463477868644755694c434a32595778315a53493649694a3958513d3d222c226e6f7465223a22227d
先将该数据进行hex转换str,得到数据如下:
{
"data": "IHsi5Yy65Z2X6ZO+5b+F6aG75qCH6K+GIjogW3si5LyB5Lia5qCH6K+GIjogIlpHREoiLCLkuJrliqHmoIfor4YiOiAi55Sf5Lqn57G75Lia5YqhIiwi5p2l5rqQ57O757ufIjogIk9BIiwi5paH5qGj5pGY6KaBIjogIjB4OTM2ODlhNzA1YWMwYmI0NjEyODI0ODgzMDYwZDczZDAyNTM0ZjhiYTc1OGY1Y2EyMWEzNDNiZWFiMmJmN2I0NyIsIuagh+etviI6ICLmoaPmoYjnsbsifV0sIuS4muWKoeagh+ivhiI6IFt7IuS4muWKoeeOr+iKgiI6ICLkuJPogYzmoaPmoYjlkZjlvZLmoaMiLCLkuJrliqHov4fnqIsiOiAi5pW055CG5b2S5qGjIn1dfQ==",
"version": "V2.0.0",
"option": "W3sia2V5IjoidGVtcGxhdGUiLCJ2YWx1ZSI6IiJ9XQ==",
"note": ""
}
再将data里的数据取出进行base64转换,得到数据如下:
{ "产品说明": { "产品编号": "PRODUCT00001", "产品说明": "产品的具体描述"}, "生产阶段": { "生产日期": "20200421", "生产环境": "温度,湿度等信息"},"质检阶段": { "质检人员": "张三", "质检时间": "20200501","质检结果": "合格","质检报告": "报告文件的hash值"}}
3.4.3上链SDK集成方案
3.4.3.1 业务流程简介
数据上链流程:
1. 用户注册过程:调用JAVA-SDK中的创建公私钥方法,生成私钥,公钥,地址。 这三项在数据库中和用户关联( 这一步是链下行为,此时和区块链之间没有交互)。
2. 业务过程上链(以在线签合同为例说明,挑选一些关键步骤进行数据上链)
2.1:订立合同, 上链内容根据系统中业务字段确定(比如:合同编号,合同名称,合同简介,时间,状态等等,编辑成json串调用SDK接口上链)。 上链后,会实时返回一笔交易hash,在数据库中和业务数据绑定。
2.2 发起方签署, 上链字段内容根据系统中业务字段确定(比如:合同编号,合同名称,合同简介,发起方,时间,状态等等) 。上链后,会实时返回一笔交易hash,在数据库中和业务数据绑定。
2.4 签署完成:上链 这一步除了之前的合同编号,合同名称,合同简介,时间,状态等,还要把签署好合同的sha256 hash一起放在json串中上链。
3.4.3.2 数据查询
1.根据上链hash值查源数据
调用区块链的查询接口,实现上链源数据查询(见:III.c 查上链源数据)
2.根据上链hash值查区块属性值
调用区块链的查询接口,实现区块属性数据查询(见:III.d 查区块属性)
注:这块查询主要查询出区块相关属性,比如区块的父hash,区块的默克尔根hash,区块高度,区块时间等属性,这些值可以结合应用系统页面进行展示,显示应用的区块链属性。
3.4.3.3. 区块链相关接口说明及样例
创建账户 newAccountLocal
函数原型:public AccountInfo newAccountLocal()
请求参数:
参数 | 类型 | 是否必填 | 说明 |
---|---|---|---|
空 | 空 | 空 | 空 |
返回字段:
返回字段 | 字段类型 | 说明 |
---|---|---|
accountInfo | AccountInfo | 账户信息,包含私钥,公钥,地址 |
样例:
- Account account = new Account();
- AccountInfo accountInfo = account.newAccountLocal();
- System.out.println("私钥:" + accountInfo.getPrivateKey());
- System.out.println("公钥:" + accountInfo.getPublicKey());
- System.out.println("地址:" + accountInfo.getAddress());
3.4.3.3.2数据上链
函数原型:
public static String createTransferTx(String privateKey, String toAddress, String execer, byte[] payLoad, long fee, long txheight)
参数 | 类型 | 是否必填 | 说明 |
---|---|---|---|
privateKey | string | 是 | 用户私钥 |
toAddress | String | 是 | 存证合约地址 |
execer | String | 是 | 存证合约名称 |
payLoad | Byte[] | 是 | 存证内容 |
fee | long | 是 | 手续费,填默认值就行 |
txheight | Long | 是 | 区块高度(用于提升查重效率) |
请求参数:
返回字段:
返回字段 | 字段类型 | 说明 |
---|---|---|
hash | String | 交易hash |
样例:
- String content = "{\"档案编号\":\"ID0000001\",\"企业代码\":\"QY0000001\",\"业务标识\":\"DA000001\",\"来源系统\":\"OA\", \"文档摘要\",\"0x93689a705ac0bb4612824883060d73d02534f8ba758f5ca21a343beab2bf7b47\"}";
- // 存证智能合约的名称(简单存证,固定就用这个名称)
- String execer = "user.write";
- // 合约地址
- String contractAddress = clientMain.convertExectoAddr(execer);
- // 取当前最大区块高度,用于设置查重范围,如果这个值不设置,默认就会从第0个高度开始查
- long txHeight = clientMain.getLastHeader().getHeight();
- // 获取签名用的私钥
- Account account = new Account();
- String privateKey = account.newAccountLocal().getPrivateKey();
- String txEncode = TransactionUtil.createTransferTx(privateKey, contractAddress, execer, content.getBytes(), TransactionUtil.DEFAULT_FEE, txHeight);
- String hash = clientMain.submitTransaction(txEncode);
- System.out.println(hash);
3.4.3.3.3查上链源数据
存证结果查询queryTransaction
函数原型:
public QueryTransactionResult queryTransaction(String hash)
请求参数:
参数 | 类型 | 是否必填 | 说明 |
---|---|---|---|
hash | String | 是 | 存证hash |
返回字段:
返回字段 | 字段类型 | 说明 |
---|---|---|
QueryTransactionResult | Object | 存证内容 |
样例:
- // String hash = "上一步上链返回的hash";
- String hash = "0xd9d201dea839898c5ba5322603603ecaa2f31438d71c1e23f8c05df9616ef473";
- QueryTransactionResult queryTransaction;
- queryTransaction = clientMain.queryTransaction(hash);
- System.out.println("交易所在的区块高度:" + queryTransaction.getHeight());
- System.out.println("区块的打包时间:" + queryTransaction.getBlocktime());
- System.out.println("从哪个用户发出:" + queryTransaction.getFromaddr());
- System.out.print("上链的数据内容" + HexUtil.hexStringToString(queryTransaction.getTx().getRawpayload()));
存证结果查询queryTransaction
函数原型:public List<BlocksResult> getBlocks(Long start, Long end, boolean isDetail)
请求参数:
参数 | 类型 | 是否必填 | 说明 |
---|---|---|---|
start | Long | 是 | 查询开始高度 |
end | Long | 是 | 查询结束高度 |
isDetial | boolean | 是 | 是否显示详情 |
返回字段:
返回字段 | 字段类型 | 说明 |
---|---|---|
List<BlocksResult> | Object | 区块内容 |
样例
- // 交易hash
- String hash = "0xd9d201dea839898c5ba5322603603ecaa2f31438d71c1e23f8c05df9616ef473";
- QueryTransactionResult queryTransaction;
- queryTransaction = clientMain.queryTransaction(hash);
- System.out.println("区块高度:" + queryTransaction.getHeight());
- System.out.println("区块时间:" + queryTransaction.getBlocktime());
- List<BlocksResult> blocks = clientMain.getBlocks(queryTransaction.getHeight(), queryTransaction.getHeight(), false);
- for (int i = 0; i < blocks.size(); i++) {
- System.out.println("默克尔根hash:" + blocks.get(i).getBlock().getTxHash());
- System.out.println("父区块hash:" + blocks.get(i).getBlock().getParentHash());
}