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

文章目录

  • 前言
  • 环境搭建
  • 项目架构
    • basic-network
    • chaincode
    • Scripts
  • 功能与展示
    • 功能列表
    • 功能展示
  • 源码

前言

很早就听说了区块链,却一直没有深入接触学习,直到动手做区块链相关的项目时才真正对区块链、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开发简易区块链Web项目_第1张图片
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上,供大家学习和参考。

你可能感兴趣的:(区块链,区块链,node.js,web开发)