基于Hyperledger Fabric开发简易区块链Web项目

前言

很早就听说了区块链,却一直没有深入接触学习,直到动手做区块链相关的项目时才真正对区块链、Hyperledger Fabric等技术有了些许了解。这篇博客主要记录的是基于Fabric开发一个简易的Web项目,如果想要对Fabric有更深入的了解,可以阅读者官方文档或以下文章:

  1. hyperledger fabric官方文档(英文)
  2. hyperledger fabric官方文档(中文)
  3. 区块链(Block Chain) Hyperledger Fabric 1.0 技术架构详解
  4. 区块链开源实现hyperledger fabric架构详解
  5. 主要参考的github项目

时间仓促,很多东西只能等空下来之后再详细补充。

环境搭建

我选择的fabric版本是Hyperledger Fabric1.4,运行环境为Ubuntu18.04,主要参考以下两个链接进行环境搭建:

  1. Hyperledger Fabric1.4环境搭建
  2. Hyperledger Fabric1.4 安装

还有一些搭建环境可能出现的问题以及解决方法,等以后补充。

项目架构

后端项目的根目录下共有三个子文件夹,分别为basic-network、chaincode和Scripts。

basic-network

basic-network文件夹存放网络配置文件。
在首次执行basic-network下的start.sh的过程中,所有网络节点已经被初始化,其中节点的配置基于basic-network下的docker-compose.yml文件。想要了解初始化的详细流程可参看startFabric.sh源码。
对这些配置文件具体作用不需要非常清晰的了解,因为这并不影响我们写项目。

chaincode

chaincode文件夹存放智能合约代码。
chaincode(智能合约)可以理解为接口,通过接口实现具体的业务操作。
Fabric的chaincode接口并不复杂,只需实现Init和Invoke方法,具体的业务实现则通过在Invoke中对输入方法名做判断分发。

package main

import (
	"encoding/json"
   	 "fmt"
	//"strconv"
  //  "strings"

    "github.com/hyperledger/fabric/core/chaincode/shim"
    pb "github.com/hyperledger/fabric/protos/peer"
)
type bMRPChainCode struct{	
}

type Patient struct{
    PatientName string `json:PatientName` //患者姓名
    PatientGender int `json:PatientGender` //患者性别
    PatientAge string `json:PatientAge` //患者年龄
    PatientNationality string `json:PatientNationality` //国籍
    PatientIDType string `json:PatientIDType` //证件类型
    PatientIDNumber string `json:PatientIDNumber` //证件编号
    PatientTelephone string `json:PatientTelephone` //电话号码
	PatientAddress string `json:PatientAddress` //住址
}

func(a *bMRPChainCode) Init(stub shim.ChaincodeStubInterface) pb.Response {
    return shim.Success(nil)
}


func(a *bMRPChainCode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
	fn,args := stub.GetFunctionAndParameters()
	if fn == "AddNewMR"{
		return a.AddNewMR(stub,args)
	}else if fn=="GetMRByID"{
        return a.GetMRByID(stub,args)
    }

    return shim.Error("Recevied unkown function invocation : "+fn)
}

func(a *bMRPChainCode) AddNewMR(stub shim.ChaincodeStubInterface, args []string) pb.Response{
	var err error
	var newRecord MedicalRecord
	//检查参数个数是否正确
	if len(args)!=7{
		return shim.Error("Incorrect number of arguments.")
	}
	newRecord.MRID=args[0]
	newRecord.MRAdmissionDate=args[1]
    newRecord.MRDischargeDate=args[2]
    newRecord.MRPaymentType=args[3]
    newRecord.MRPatientID=args[4]
    newRecord.MRDoctors =args[5]
	newRecord.MRDiagnosis =args[6]
	
	ProInfosJSONasBytes,err := json.Marshal(newRecord)
    if err != nil{
        return shim.Error(err.Error())
    }

    err = stub.PutState(newRecord.MRID,ProInfosJSONasBytes)
    if err != nil{
        return shim.Error(err.Error())
    }
    return shim.Success(nil)
}

func(a *bMRPChainCode) GetMRByID(stub shim.ChaincodeStubInterface,args []string) pb.Response{
    
    if len(args) != 1{
        return shim.Error("Incorrect number of arguments.")
    }
    MRID := args[0]
    resultsIterator,err := stub.GetHistoryForKey(MRID)
    if err != nil {
        return shim.Error(err.Error())
    }
    defer resultsIterator.Close()
    
    //var foodProInfo ProInfo
    var medicalRecord MedicalRecord

    for resultsIterator.HasNext(){
        var _medicalRecord MedicalRecord
        //var FoodInfos FoodInfo
        response,err :=resultsIterator.Next()
        if err != nil {
            return shim.Error(err.Error())
        }
        json.Unmarshal(response.Value,&_medicalRecord)
        if _medicalRecord.MRID != ""{
            medicalRecord = _medicalRecord
            continue
        }
    }
    jsonsAsBytes,err := json.Marshal(medicalRecord)   
    if err != nil {
        return shim.Error(err.Error())
    }
    return shim.Success(jsonsAsBytes)
}

func main(){
     err := shim.Start(new(bMRPChainCode))
     if err != nil {
         fmt.Printf("Error starting bMRP chaincode: %s ",err)
     }
}

Scripts

Scripts文件夹存放nodejs代码。
Hyperledger交易验证流程如下图所示,可以看到,在合约编写完毕后,还差应用层调用。
Hyperledger交易验证流程
Fabric主要提供了两种方式实现应用层调用,一种是通过cli容器入口,另一种是sdk。Fabric官网支持多种sdk,官方发布的有node,java,除此之外还有Go,Python等sdk,活跃度都很高。本文使用的是node sdk进行应用程序开发。

'use strict';
/*
 * Channel Name : mychannel
 * chaincodeId : bMRPCC
 */

var Fabric_Client = require('fabric-client');
var Fabric_CA_Client = require('fabric-ca-client');
var path = require('path');
var util = require('util');

function RegisterNewAdmin() {
	var fabric_client = new Fabric_Client();
	var fabric_ca_client = null;
	var admin_user = null;
	var store_path = path.join(__dirname, 'hfc-key-store');
	console.log(' Store path:' + store_path);

	// create the key value store as defined in the fabric-client/config/default.json 'key-value-store' setting
	Fabric_Client.newDefaultKeyValueStore({
		path: store_path
	}).then((state_store) => {
		// assign the store to the fabric client
		fabric_client.setStateStore(state_store);
		var crypto_suite = Fabric_Client.newCryptoSuite();
		// use the same location for the state store (where the users' certificate are kept)
		// and the crypto store (where the users' keys are kept)
		var crypto_store = Fabric_Client.newCryptoKeyStore({
			path: store_path
		});
		crypto_suite.setCryptoKeyStore(crypto_store);
		fabric_client.setCryptoSuite(crypto_suite);
		var tlsOptions = {
			trustedRoots: [],
			verify: false
		};
		// be sure to change the http to https when the CA is running TLS enabled
		fabric_ca_client = new Fabric_CA_Client('http://localhost:7054', tlsOptions, 'ca.example.com', crypto_suite);

		// first check to see if the admin is already enrolled
		return fabric_client.getUserContext('admin', true);
	}).then((user_from_store) => {
		if (user_from_store && user_from_store.isEnrolled()) {
			console.log('Successfully loaded admin from persistence');
			admin_user = user_from_store;
			return null;
		} else {
			// need to enroll it with CA server
			return fabric_ca_client.enroll({
				enrollmentID: 'admin',
				enrollmentSecret: 'adminpw'
			}).then((enrollment) => {
				console.log('Successfully enrolled admin user "admin"');
				return fabric_client.createUser({
					username: 'admin',
					mspid: 'Org1MSP',
					cryptoContent: {
						privateKeyPEM: enrollment.key.toBytes(),
						signedCertPEM: enrollment.certificate
					}
				});
			}).then((user) => {
				admin_user = user;
				return fabric_client.setUserContext(admin_user);
			}).catch((err) => {
				console.error('Failed to enroll and persist admin. Error: ' + err.stack ? err.stack : err);
				throw new Error('Failed to enroll admin');
			});
		}
	}).then(() => {
		console.log('Assigned the admin user to the fabric client ::' + admin_user.toString());
	}).catch((err) => {
		console.error('Failed to enroll admin: ' + err);
	});
}

代码有点长,故此处只展示了注册新管理员的函数功能。完成简单封装之后,其他部分就与大多数nodejs应用开发没什么区别啦。

功能与展示

由于做的是一个前后端分离的小demo,前端项目一时找不到了……以后补

功能列表

  1. 注册管理员。
  2. 注册用户。
  3. 新增病历。
  4. 查询病历。

功能展示

(以后补)

源码

项目源码已经放在Github上,供大家学习和参考。

评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值