基于Hyperledger Fabric的学位学历认证管理系统
1.部署环境
- Linux Ubuntu(root 用户)
- Golang 语言 go1.12以上均可
- docker 18.09.7及以上
- docker-compose 1.22.0及以上
2.环境配置
2.1安装 docker 以及 docker-compose
使用 docker 可以方便的解决程序依赖的环境问题;并且后续需要使用到的 Hyperledger Fabric 框架官方也提供了相应的 docker 的容器。 安装 docker 命令:
1
| sudo apt install docker.io
|
好的下载完毕,为了验证 docker 成功安装结果输入下面命令
为了方便管理多个 docker 容器,还需要安装 docker-compose:
1
| sudo apt install docker-compose
|
好的下载完毕,为了验证 docker-compose是否成功安装结果输入下面命令
2.2安装 golang
区块链框架Hyperledger Fabric 目前支持Java、Go 等主流编程语言并提供了相应的SDK,但是支持最全面的还是 Golang,因此采用 Go 语言来进行开发是比较好的选择;
安装Golang:
1
| wget https://go.dev/dl/go1.17.9.linux-amd64.tar.gz
|
使用 tar 命令将压缩文件解压至指定路径/usr/local/下(注意:请切换root用户下再进行解压不然会报权限不足的错误)
1
| tar -zxvf go1.17.9.linux-amd64.tar.gz -C /usr/local
|
sudo vim /etc/hosts,添加(没有vim可以使用vi):
1 2 3
| 127.0.0.1 orderer.example.com 127.0.0.1 peer0.org1.example.com 127.0.0.1 peer1.org1.example.com
|
保存退出。
sudo gedit /root/.bashrc文件,设置环境变量 GOHOME 以及 GOROOT:
1 2 3
| export GOPATH=/root/go export GOROOT=/usr/local/go export PATH=$GOROOT/bin:$PATH
|
激活环境变量:
验证安装成功,使用 go version 结果如图所示:
3.项目部署
创建保存项目的文件夹:
进入文件夹:
从 github 仓库克隆项目到education文件夹:
1
| git clone https://github.com/Pistachiout/Academic-Degree-BlockChain.git education [https://github.com/sxguan/education.git]
|
赋予权限并进入项目目录:
1
| sudo chmod -R +x ./education && cd education
|
添加项目开发需要依赖的 Golang 包:
命令可能会执行失败,此时设置代理即可:
1
| go env -w GOPROXY=https://goproxy.cn
|
4.启动项目
由于每次启动流程相对固定,首先进入root用户,并配置环境,然后启动项目在项目的目录下运行 clean_docker.sh 脚本即可启动项目:
1 2 3 4 5 6 7 8 9 10
| sudo -s export GOPATH=/root/go export GOROOT=/usr/local/go export PATH=$GOROOT/bin:$PATH source /root/.bashrc cd $GOPATH/src/education export GO111MODULE=on export GOPROXY=https://goproxy.io go mod tidy ./clean_docker.sh
|
再开一个终端,按以下步骤启动Fabric浏览器
1 2 3 4 5 6 7
| sudo -s export GOPATH=/root/go export GOROOT=/usr/local/go export PATH=$GOROOT/bin:$PATH source /root/.bashrc cd $GOPATH/src/education/explorer docker-compose -f docker-compose.yaml up -d
|
使用docker-compose down -v关闭浏览器,Ctrl + C 停止Web服务
(浏览器启动)
通过浏览器访问localhost:8080即可进入 区块链浏览器
(数据库系统启动)
通过浏览器访问 localhost:9000端口即可进入 web 端,结果如图所示:
CouchDB 数据库1:peer0节点7051端口 http://localhost:5984/_utils
CouchDB 数据库2:peer1节点9051端口 http://localhost:7984/_utils
关闭项目
Ctrl+C即可
重启项目
./clean_docker.sh(会清除所有数据)
停止项目(会清除所有数据)
1 2 3 4 5
| sudo docker rm -f $(sudo docker ps -aq) sudo docker network prune sudo docker volume prune cd fixtures docker-compose down -v && docker-compose down --rmi all
|
强行删除容器
1 2
| docker stop $(docker ps -aq) docker rm $(docker ps -aq)
|
5、目录结构
config.yaml
配置文件:描述组织结构、通道结构、所有配置文件、证书的路径
chaincode
:链码文件所在的目录
vendor
:依赖包
edu.go
:链码(智能合约)
fixtures
:fabric基础网络所有文件所在的目录
img
:存放前端的图片
sdkInit
:sdk核心代码
sdkSetting.go
:实现了包括Setup、CreateAndJoinChannel、CreateCCLifecycle等基础功能的函数
integration.go
:实现了包括DiscoverLocalPeers、InitService等基础功能的函数
sdkInfo.go
:定义了一些组织和SDK中用到的结构体
set/get.go
:复制调用链码的两个函数
service
:服务端代码,封装了合约接口的调用
explorer
:区块链浏览器文件及配置
6、接口
(修改以上项目)
1、测试上链
(1)添加结构体
1 2 3 4
| type User struct { UserName string `json:"username"` Password string `json:"password"` }
|
(2) 添加service层调用链码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| func (t *ServiceSetup) SaveUser(user User) (string, error) {
eventID := "eventAddUser" reg, notifier := regitserEvent(t.Client, t.ChaincodeID, eventID) defer t.Client.UnregisterChaincodeEvent(reg)
b, err := json.Marshal(user) if err != nil { return "", fmt.Errorf("指定的edu对象序列化时发生错误") }
req := channel.Request{ChaincodeID: t.ChaincodeID, Fcn: "addUser", Args: [][]byte{b, []byte(eventID)}} respone, err := t.Client.Execute(req) if err != nil { return "", err }
err = eventResult(notifier, eventID) if err != nil { return "", err }
return string(respone.TransactionID), nil }
func (t *ServiceSetup) FindUserInfoByUsername(username string) ([]byte, error){
req := channel.Request{ChaincodeID: t.ChaincodeID, Fcn: "queryUserInfoByUsername", Args: [][]byte{[]byte(username)}} respone, err := t.Client.Query(req) if err != nil { return []byte{0x00}, err }
return respone.Payload, nil }
|
(3)智能合约
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
| type User struct { UserName string `json:"username"` Password string `json:"password"` }
func PutUser(stub shim.ChaincodeStubInterface, user User) ([]byte, bool) { b, err := json.Marshal(user) if err != nil { return nil, false }
err = stub.PutState(user.UserName, b) if err != nil { return nil, false } return b, true }
func GetUserInfo(stub shim.ChaincodeStubInterface, username string) (User, bool) { var user User b, err := stub.GetState(username) if err != nil { return user, false }
if b == nil { return user, false }
err = json.Unmarshal(b, &user) if err != nil { return user, false } return user, true }
func (t *EducationChaincode) addUser(stub shim.ChaincodeStubInterface, args []string) peer.Response {
if len(args) != 2{ return shim.Error("给定的参数个数不符合要求") }
var user User err := json.Unmarshal([]byte(args[0]), &user) if err != nil { return shim.Error("反序列化信息时发生错误") }
_, exist := GetUserInfo(stub, user.UserName) if exist { return shim.Error("要添加的身份证号码已存在") }
_, bl := PutUser(stub, user) if !bl { return shim.Error("保存信息时发生错误") }
err = stub.SetEvent(args[1], []byte{}) if err != nil { return shim.Error(err.Error()) }
return shim.Success([]byte("信息添加成功")) }
func (t *EducationChaincode) queryUserInfoByUsername(stub shim.ChaincodeStubInterface, args []string) peer.Response { if len(args) != 1 { return shim.Error("给定的参数个数不符合要求") }
b, err := stub.GetState(args[0]) if err != nil { return shim.Error("根据name查询信息失败") }
if b == nil { return shim.Error("根据name没有查询到相关的信息") }
var user User err = json.Unmarshal(b, &user) if err != nil { return shim.Error("反序列化user信息失败") }
result, err := json.Marshal(user) if err != nil { return shim.Error("序列化user信息时发生错误") } return shim.Success(result) }
|