/marriage-demo

基于fisco bcos 区块链的结婚证书系统

Primary LanguageCSS

FISCO BCOS Block Chain Marray System

All Contributors

FISCO BCOS Block Chain Marry System created by Shanghai JiuYu Software Systems Co,Ltd.

由上海久誉软件系统有限公司研发的针对基于 FISCO BCOS 的登记结婚系统。

111

1. 设计理念

基于区块链的登记结婚系统是对结合Character角色合约与Evidence存证合约打造结婚证书的实现,利用WeBASE管理平台将合约导出为Java项目。

2. 目标用户

基于区块链的登记结婚系统目标用户是民政局工作人员。

3. 系统架构

供应链金融

3.1 系统功能

image

3.2 具体业务层

新建证书流程图

image

用户签名流程图

image

4. 使用WeBASE平台导出Java项目

4.1 上传合约

image

合约源码:MarriageEvidence.sol

pragma solidity^0.4.25;
import "./EvidenceFactory.sol";
import "./Character.sol";

contract MarriageEvidence{
    address private admin;
    mapping(address=>bool) private isMarried; //角色是否已经结婚,避免重婚
    mapping(address=>bool) private isSigned;  //证书中角色是否已经签字
    mapping(address=>bool) private eviSigned; //证书中成员是否均已经达成签字,全部签字为true,非全部均为false
    string private certificateNumber; //证书编号
    mapping(string=>address) private characterAddress; //角色合约地址
    mapping(string=>address) private eviContractAddress;//存证合约地址
    mapping(string=>address) private eviAddress;//证书地址
    
    constructor() public {
        admin = msg.sender;
    }
    
    modifier adminOnly{  
        require(msg.sender == admin ,"require admin");
        _;
    }
    
    modifier checkMarried(address _husband,address _wife){
        require(isMarried[_husband] == false,"Husband is married");
        require(isMarried[_wife] == false,"Wife is married");
        _;
    }
    

    function deployEvi(string _certificateNumber,address _husband,string _husbandSummary,address _wife,string _wifeSummary,address _witness,bytes32 _witnessName) external adminOnly checkMarried(_husband,_wife){
        Character cha = new Character();
        characterAddress[_certificateNumber]= address(cha);
        cha.addCharacter(msg.sender,"民政局");
        cha.addCharacter(_husband,_husbandSummary);
        cha.addCharacter(_wife,_wifeSummary); 
        cha.addCharacter(_witness,bytes32ToString(_witnessName));
        EvidenceFactory evi = new EvidenceFactory(cha.getAllCharater());
        eviContractAddress[_certificateNumber]=address(evi);
    }
    
    function newEvi(string _certificateNumber,string _evi)external adminOnly returns(bool){
        eviAddress[_certificateNumber]=EvidenceFactory(eviContractAddress[_certificateNumber]).newEvidence(_evi);
        isSigned[msg.sender] = true;
        return true;
    }
    
    function sign(string _certificateNumber) external returns(bool) {
            require(EvidenceFactory(eviContractAddress[_certificateNumber]).verify(msg.sender),"you not is signer");
            isSigned[msg.sender] = EvidenceFactory(eviContractAddress[_certificateNumber]).addSignatures(eviAddress[_certificateNumber]);
            return isSigned[msg.sender];
    }
    
    function checkSigned(string _certificateNumber)internal returns(bool){
        address[] memory characters;
        characters = Character(characterAddress[_certificateNumber]).getAllCharater();
        for(uint256 i=0;i<characters.length;i++){
            if(isSigned[characters[i]] == false){
                return false;
            }
        }
        return true;
    }
    
    function checkEviSigned(string _certificateNumber)public constant 
        returns(bool){
            if(checkSigned(_certificateNumber) == true){
                eviSigned[eviAddress[_certificateNumber]] = true;
                isMarried[Character(characterAddress[_certificateNumber]).getAllCharater()[1]] = true;
                isMarried[Character(characterAddress[_certificateNumber]).getAllCharater()[2]] = true;
            }
        return eviSigned[eviAddress[_certificateNumber]];
    }
    
    function getEvi(string _certificateNumber) public constant 
        returns(string,address[],address[]){
        return EvidenceFactory(eviContractAddress[_certificateNumber]).getEvidence(eviAddress[_certificateNumber]);
    }
    
    function getEviCharacter(string _certificateNumber) public constant 
        returns(address,string,address,string,address,string){
            address Husband;
            address Wife;
            address Witness;
            address[] memory characters;
            string memory HusbandSummary;
            string memory WifeSummary;
            string memory WitnessSummary; 
            characters = Character(characterAddress[_certificateNumber]).getAllCharater();
            Husband = characters[1];
            Wife = characters[2];
            Witness = characters[3];
            HusbandSummary = Character(characterAddress[_certificateNumber]).seekCharacter(Husband);
            WifeSummary = Character(characterAddress[_certificateNumber]).seekCharacter(Wife);
            WitnessSummary = Character(characterAddress[_certificateNumber]).seekCharacter(Witness);
        return(Husband,HusbandSummary,Wife,WifeSummary,Witness,WitnessSummary);
    }
    
    function bytes32ToString(bytes32 x) constant internal returns(string){
        bytes memory bytesString = new bytes(32);
        uint charCount = 0 ;
        for(uint j = 0 ; j<32;j++){
            byte char = byte(bytes32(uint(x) *2 **(8*j)));
            if(char !=0){
                bytesString[charCount] = char;
                charCount++;
            }
        }
        bytes memory bytesStringTrimmed = new bytes(charCount);
        for(j=0;j<charCount;j++){
            bytesStringTrimmed[j]=bytesString[j];
        }
        return string(bytesStringTrimmed);
    }

}

4.2 编译合约

image

4.3 部署合约

image

4.4 导出Java项目

选择节点、用户及需要导出的合约

image

image

4.5 查看目录结构

  • mian目录下存放执行代码
  • conf目录下存放节点证书配置

image

4.6 将Gradle项目转为Maven项目

image

4.7 导入Maven依赖

    <dependencies>
        <dependency>
            <groupId>org.fisco-bcos.java-sdk</groupId>
            <artifactId>fisco-bcos-java-sdk</artifactId>
            <version>2.7.2</version>
        </dependency>

        <dependency>
            <groupId>com.webank</groupId>
            <artifactId>webase-app-sdk</artifactId>
            <version>1.5.1-SNAPSHOT</version>
        </dependency>

5. 程序运行

5.1 前置工作

首先需要搭建FISCO BCOS链与WeBASE服务,本案例通过WeBASE一键部署搭建4节点的链和WeBASE服务

参考WeBASE官方提供的文档,详情请参见:https://webasedoc.readthedocs.io/zh_CN/latest/docs/WeBASE/install.html

  • :部署WeBASE时注意修改一键部署的配置文件common.properties中修改node.counts=4来搭建4节点的链

5.2 依赖安装

maven安装

# 下载maven包
wget https://mirrors.bfsu.edu.cn/apache/maven/maven-3/3.8.1/binaries/apache-maven-3.8.1-bin.tar.gz
# 解压
tar -zxvf apache-maven-3.8.1-bin.tar.gz
# 重命名
mv apache-maven-3.8.1-bin maven
# 查看当前路径
cd maven/
pwd
	/data/home/webase/maven

# 配置mvn环境变量
vi /etc/profile 

	export MAVEN_HOME=/data/home/webase/maven
	export PATH=$PATH:$MAVEN_HOME/bin

# 更新环境变量
source /etc/profile 
# 检查mvn命令,正常输出版本号则安装成功
mvn -v

npm安装

# 下载node包
wget https://npm.taobao.org/mirrors/node/v10.16.2/node-v10.16.2-linux-x64.tar.gz
# 解压
tar -zxvf node-v10.16.2-linux-x64.tar.gz
# 重命名
mv node-v10.16.2-linux-x64 node
# 查看路径
cd node/
pwd
	/data/home/webase/node

# 配置node环境变量
vi /etc/profile 

	export NODE_HOME=/data/home/webase/node
	export PATH=$PATH:$NODE_HOME/bin

# 更新环境变量
source /etc/profile 

# 检查npm命令,正常输出版本号则安装成功
npm -v

mysql创建数据库

以mysql用户为root为例,创建marriage_demo的数据库

mysql -uroot -p -e "create database marriage_demo"

5.3 拉取代码

1 拉取代码

git clone https://github.com/jiuyu-software/marriage-demo

项目源码中包含frontend前端代码和backend后端代码,目录结构如下

cd marriage-demo/
ls
├── backend
│   └── marriage-demo
├── frontend
│   └── marriage
└── README.md

5.4 前端代码部署

前端代码基于VUE编写

1 修改配置文件

进入frontend/marriage目录,修改配置文件vue.config.jsproxy,连接上文的backend后端服务

cd frontend/marriage
vi vue.config.js
# 以同机运行为例,后端服务同机运行,IP为127.0.0.1,端口为8080
# 修改proxy.target的值为对应的IP:PORT
    proxy: {
      // change xxx-api/login => mock/login
      // detail: https://cli.vuejs.org/config/#devserver-proxy
      [process.env.VUE_APP_BASE_API]: {
//        target: "http://**.**.**.**:**",
        target: "http://127.0.0.1:8080",
        changeOrigin: true,
        pathRewrite: {
          ["^" + process.env.VUE_APP_BASE_API]: "",
        },
      },
    },

2 编译并运行

# 安装前端依赖包
npm install
# 运行
npm run dev
# 运行成功后显示
  App running at:
    - Local:   http://localhost:9528 
    - Network: http://127.0.0.2:9528 # 127.0.0.2为内网或公网IP

    Note that the development build is not optimized.
    To create a production build, run npm run build.

我们可以通过在浏览器中访问http://127.0.0.2:9528即可访问

  • 此处浏览器访问的URL和下文后端服务中配置的appLink需保持一致
  • 如果通过npm build构建静态文件 + nginx重定向的方式加载前端,需要修改nginx.conf中访问后端服务的IP端口,并对外暴露9528端口即可。
  • 需要部署下文的后端服务后,才可正常操作页面。具体操作步骤可以参考下文的运行演示

5.5 后端代码部署

后端代码是基于SpringBoot工程

1 执行sql脚本

cd backend/marriage-demo/src/main/resources/db
# 通过mysql -e命令执行.sql脚本,以root用户,db名为marriage_demo为例
mysql -uroot -p  -D marriage -e "source ./marriage_demo.sql"

2 部署MarriageEvidence合约

cd backend/marriage-demo/src/main/resources/contracts/MarriageEvidence.sol
cat MarriageEvidence.sol

将合约内容复制记录,随后到WeBASE中部署

进入WeBASE管理台

  • 创建WeBASE私钥:在“私钥管理”中创建一个新的私钥用户,记录其signUserId,如:d0fb7d6c9fa04ef484e10f4bf5b34426
  • WeBASE的“合约管理-合约IDE”中,创建MarriageEvidence的合约,粘贴上文的MarriageEvidence.sol内容,并编译,部署合约,记录合约地址,如:0xbbac4362f59a8ffe78ef4585460e9236c02b6c48

3 WeBASE应用接入

点击“应用管理”,若该案例已集成在WeBASE,则选择模板,在注册信息里面可获得WeBASE-Node-Manager的 IP,Port,appKey,appSecret 相关信息,记录这些信息,在下文的的application.properties配置中会用到 image

4 修改application.properties文件

cd backend/marriage-demo/src/main/resources/application.properties
vi application.properties
  • 修改配置文件的mysql连接配置
  • 修改WeBASE-Front和WeBASE-Node-Manager的配置
  • 修改WeBASE节点地址端口
  • 修改本案例的前端访问URL配置
  • 修改MarriageEvidence合约的配置
spring.application.name=marriage-demo
# WeBASE 节点地址及端口
system.peers=127.0.0.1:20200
system.groupId=1
# WeBASE 节点证书配置目录
system.certPath=conf,config,src/main/resources/conf,src/main/resources/config
# MarriageEvidence 合约部署用户
system.hexPrivateKey=f7ee38ca3715130c164f140ef5751828a0aa8aab60198d26df4e7fd26d843233
# MarriageEvidence.sol 合约部署地址
system.contract.marriageEvidenceAddress=0x62a25adcae9f0f9ce22caa35bc6fb596c8476f71

# 服务默认端口,若修改,需要在前端访问后端时对应修改
server.port=8080
server.session.timeout=60
banner.charset=UTF-8

# 数据库相关信息
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/marriage_demo
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.minimum-idle=10
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.auto-commit=true
spring.datasource.hikari.idle-timeout=600000
spring.datasource.hikari.pool-name=DateSourceHikariCP
spring.datasource.hikari.max-lifetime=1800000
spring.datasource.hikari.connection-test-query=SELECT 1

# webase-node-mgr的IP与端口
webase.node.mgr.url=http://127.0.0.1:5001
# webase管理台-应用管理,创建自定义应用生成的appKey
webase.node.mgr.appKey=kkCbsvFG
# webase管理台-应用管理,创建自定义应用生成的appSecret
webase.node.mgr.appSecret=RcMHpkp5nVWSgFxsTtzLkE79pKZvvUMV
# 是否加密传输
webase.node.mgr.isTransferEncrypt=true

#pagehelper分页插件配置
pagehelper.helperDialect=mysql
pagehelper.reasonable=true
pagehelper.supportMethodsArguments=true
pagehelper.params=count=countSql
# page-size-zero:默认值为 false,当该参数设置为 true 时,如果 pageSize=0 或者 RowBounds.limit = 0 就会查询出全部的结果
#pagehelper.page-size-zero=true

# mybatis-plus 配置
mybatis-plus.mapper-locations=classpath:/mapper/**/*.xml

# 日志配置
logging.config=classpath:logback-boot.xml

# 打印日志级别
logging.level.root=DEBUG

# 返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8

5 编译打包

编译项目,回到backend/marriage-demo目录,目录下有pom.xml的maven配置文件

cd backend/marriage-demo
# maven 编译
mvn clean package -Dmaven.test.skip=true

打包完成后会得到target目录

  • 若修改了application.properties,需要重新打包并运行

6 运行

运行得到的jar

cd target/
nohup java -jar marrage-demo-0.0.1-SNAPSHOT.jar &

查看日志

tail -f logs/log/marriage-demo.log

当我们看到日志持续输出日志时即运行成功,就可以访问上文中部署的前端页面({IP}:9528)进行操作了

6.运行演示

演示地址: 基于区块链的供应链支付结算管理平台: http://marriage.icoding721.com 账户密码:admin/123456

WeBASE管理平台:http://supplychain.icoding721.com:5000/ 账密:admin/Abcd12345

演示登记结婚及签名过程

6.1 进入首页

image

6.2 管理员登录

image

6.3 新建用户并添加用户信息

image

注:需要添加男方、女方、证婚人三方用户信息

6.4 新建证书

在证书列表中,点击新建证书

image

在列表中,选择男方、女方及证婚人,点击提交

image

注:提交后证书状态为草稿状态,需各方完成签名后,就改为已确认状态。

image

6.5 用户签名

在用户信息列表中找到需要签名的用户,点击签名操作,签名成功返回交易Hash

image

注:男方、女方、证婚人都需完成签名

6.6 查看证书

在证书列表中,可以查看证书详情及签名状态

image

6.7 查看WeBASE管理平台交易情况

image

7.接入WeBASE过程介绍

7.1 WeBASE接入原理

本案例通过引入webase-app-sdk,并在项目的application.properties传入WeBASE应用管理中生成的配置信息即可。

1 引入webase-app-sdk

本案例使用WeBASE提供的应用接入SDKwebase-app-sdk接入WeBASE,在本案例的springboot pom.xml文件中已经集成

<dependency>
	<groupId>com.webank</groupId>
	<artifactId>webase-app-sdk</artifactId>
	<version>1.5.1-SNAPSHOT</version>
</dependency>

通过该SDK,我们可以在项目中调用WeBASE的接口,进行合约和私钥等数据的托管。如下文所示

2 获取WeBASE应用接入配置

我们可以登录WeBASE 管理平台获取应用接入的配置信息

点击“应用管理”,若该案例已集成在WeBASE,则选择模板,在注册信息里面可获得IP,Port,appKey,appSecret 相关信息,拿到这些信息会放到java配置文件本案例中的application.properties image

在配置了上述信息后,只要启动本案例的前后端服务,即完成了供应链服务接入WeBASE的操作,在WeBASE的“应用管理”中访问本案例的管理页面

7.2通过WeBASE管理私钥与合约

WeBASE管理平台在私钥管理可以查看通过业务系统注册的用户相关信息 image

Contributors


皮卡丘的猫

💻

shitou

💻

ChengL

💻

总结

此登记结婚案例是基于fisco bcos平台开发的一个比较简单的DApp,后续扩展的功能包括

1.部分功能需优化(持续开发中....)

如有不足之处,还请各方不吝赐教。感谢~~

如有商业合作,可以邮件至:he_jiebing@jiuyv.com