使用Hyperledger Composer和React.js构建保险应用程序-part1

本教程介绍如何使用HyperledgeComposer和React.js作为前端Web应用程序构建简单的HyperledgeFabric网络。

该应用程序是一个保险应用程序,利用区块链以更有效和安全的方式执行风险分析。

Hyperledger Composer

Hyperledger composer是一种在Hyperledger Fabric上构建区块链业务网络的工具。它抽象了构建区块链网络所涉及的许多复杂性。

要安装Hyperledger Composer,请按照此处的说明进行操作。https://hyperledger.github.io/composer/latest/installing/installing-prereqs

概念

本系统是一种保险应用程序,利用区块链进行更高效和安全的风险分析。这是通过保单持有人在保险公司之间传递数据来实现的,这意味着保单持有人将其数据提供给保险公司。

系统中有两个参与者:

· 投保人
· 保险公司

系统的保单持有人希望执行以下操作:

· 添加资产
· 接受/拒绝保险报价
· 提出索赔

保险公司希望能够执行以下操作:

· 查看对保险开放的资产
· 提供保险
· 批准/拒绝索赔

投保人是购买保险的个人,或者已经为房产或汽车等资产购买了保险的投保人。

保险公司是向保单持有人提供此保险的公司。与保单持有人寻找保险公司的传统保险不同,保单持有人在区块链网络输入他们的保险需求,保险公司提供报价。

风险分析使用链码来实现,当投保人寻找保险时,链码将自动运行。与过去的索赔和资产相关的所有数据均由保单持有人拥有和承载不同,信息不会单独留在保险公司。这些数据将用于对投保人及其资产要求进行简单评估,以提供一个风险分析评分,保险公司可使用该评分来通知其保险报价。

由于此系统将包含我们不希望每个人都看到的敏感数据,因此我们需要实现控制对此个人数据的访问权限。

让我们首先创建一个用于存储应用程序的文件夹:

mkdir insurance-application && cd insurance-application

现在让我们使用yeoman为我们的业务网络创建一个框架

yo hyperledger-composer:businessnetwork

Business network name: risk-analysis-tutorial
Description: 分线分析教程
Author name: 你的名字
Author email: 你的邮箱
License: Apache-2.0
Namespace: org.acme.riskanalysis

Do you want to generate an empty template network? (您想生成空模板网络吗?)Yes

cd risk-analysis-tutorial

运行命令ls应显示以下文件夹和文件:

– models
– package.json
– permissions.acl
– README.md

因此,首先我们要定义我们网络中的参与者,谁是投保人和谁是保险人。我们打开文件org.acme.riskanalysis.cto,可以在models文件夹中找到该文件。

participant Policyholder identified by id {
o String id
o String name
o Double balance default = 0.0
o Integer noClaimsYears default = 0
}

participant InsuranceCompany identified by id {
o String id
o String name
o Double balance default = 0.0
o Integer insuranceContracts default = 0
}

保单持有人还希望能够添加房屋或汽车等资产,因此我们定义一个名为PrivateAsset的资产以及一个名为AssetType的枚举。PrivateAsset依赖于将成为创建资产的人的保单持有人。只有3种类型的资产可以添加到系统中,由enum AssetType定义。

asset PrivateAsset identified by id {
o String id
o AssetType assetType
o Double value
–> Policyholder policyholder
o Integer durationInMonths
o Double riskAnalysisScore default = 0.0
–> InsuranceCompany insuranceCompany optional
}

enum AssetType {
o CAR
o HOUSE
o PHONE
}

我们将增加两种资产类型,一种用于保险报价,另一种用于索赔。

asset InsuranceOffer identified by id {
o String id
–> Policyholder policyholder
–> InsuranceCompany insuranceCompany
–> PrivateAsset privateAsset
o Double monthlyCost
o Integer durationInMonths
o String status default = “pending”
}

asset Claim identified by id {
o String id
–> PrivateAsset privateAsset
–> Policyholder policyholder
–> InsuranceCompany insuranceCompany
o String description
o Double claimValue
o String status default = “pending”
}

接下来,我们需要为网络定义我们的事务。

transaction AcceptInsuranceOffer {
–> InsuranceOffer offer
}

transaction MakeInsuranceOffer {
–> Policyholder policyholder
–> InsuranceCompany insuranceCompany
–> PrivateAsset privateAsset
o Double monthlyCost
}

transaction RiskAnalysis {
–> PrivateAsset privateAsset
}

transaction CreateNewAsset {
–> Policyholder policyholder
o AssetType assetType
o Double value
o Integer durationInMonths
}

transaction CreateClaim {
–> PrivateAsset privateAsset
–> Policyholder policyholder
o String description
o Double claimValue
}

transaction ProcessClaim {
–>Claim claim
o String status
}

您的完整模型文件应该是这样的:

/*
* Licensed under the Apache License, Version 2.0 (the “License”);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an “AS IS” BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace org.acme.riskanalysis

participant Policyholder identified by id {
o String id
o String name
o Double balance default = 0.0
o Integer noClaimsYears default = 0
}

participant InsuranceCompany identified by id {
o String id
o String name
o Double balance default = 0.0
o Integer insuranceContracts default = 0
}

asset PrivateAsset identified by id {
o String id
o AssetType assetType
o Double value
–> Policyholder policyholder
o Integer durationInMonths
o Double riskAnalysisScore default = 0.0
–> InsuranceCompany insuranceCompany optional
}

enum AssetType {
o CAR
o HOUSE
o PHONE
}

asset InsuranceOffer identified by id {
o String id
–> Policyholder policyholder
–> InsuranceCompany insuranceCompany
–> PrivateAsset privateAsset
o Double monthlyCost
o Integer durationInMonths
o String status default = “pending”
}

asset Claim identified by id {
o String id
–> PrivateAsset privateAsset
–> Policyholder policyholder
–> InsuranceCompany insuranceCompany
o String description
o Double claimValue
o String status default = “pending”
}

transaction AcceptInsuranceOffer {
–> InsuranceOffer offer
}
transaction MakeInsuranceOffer {
–> Policyholder policyholder
–> InsuranceCompany insuranceCompany
–> PrivateAsset privateAsset
o Double monthlyCost
}

transaction RiskAnalysis {
–> PrivateAsset privateAsset
}

transaction CreateNewAsset {
–> Policyholder policyholder
o AssetType assetType
o Double value
o Integer durationInMonths
}

transaction CreateClaim {
–> PrivateAsset privateAsset
–> Policyholder policyholder
o String description
o Double claimValue
}

transaction ProcessClaim {
–>Claim claim
o String status
}

接下来,我们需要实现我们的事务,这是链代码完成。

首先,您需要在Hyperledger编写器项目的主目录中创建一个名为lib的新目录,您将在其中看到诸如package.json之类的文件,或者如果您位于终端的models目录中,请运行以下命令:

mkdir ../lib/ && cd ../lib/

在lib目录中,我们现在将创建一个名为logic.js的新文件,它将成为我们的链代码文件。

让我们首先实现CreateNewAsset。您会注意到我们使用decorators 为我们的函数提供元数据。

/**
* Create a new asset
* @param {org.acme.riskanalysis.CreateNewAsset} asset
* @transaction
*/
async function createNewAsset(asset) {
let assetRegistry = await getAssetRegistry(‘org.acme.riskanalysis.PrivateAsset’);
var factory = getFactory()

num_id = (Math.floor(Math.random() * ( 999999 – 100000) + 100000)).toString(10)

var assetID = asset.policyholder.id + num_id;
var newAsset = factory.newResource(‘org.acme.riskanalysis’, ‘PrivateAsset’, assetID)
newAsset.policyholder = asset.policyholder;
newAsset.assetType = asset.assetType;
newAsset.value = asset.value;
newAsset.durationInMonths = asset.durationInMonths;

await assetRegistry.add(newAsset)
}

现在我们已经完成了,让我们测试一下它是否有效。

首先,我们必须启动Hyperledger Fabric。找到您已下载的目录并运行脚本,如下所示:

cd ~/fabric-dev-servers
./startFabric.sh
./createPeerAdminCard.sh

注意,如果以前没有运行过Hyperledger Fabric或以前运行过/teardownfabric.sh,则只需运行/createpeeradincard.sh。

nvm use 8

现在回到risk-analysis-tutorial目录,我们将开始部署我们网络的0.0.1版本。

运行此命令将生成名为[email protected]的业务网络归档文件

composer archive create -t dir -n .

接下来,我们将在我们设置的Hyperledger Fabric对等设备上安装Composer业务网络

composer network install –card [email protected]
–archiveFile [email protected]

现在我们可以开始我们的业务网络,这可能需要几分钟

composer network start –networkName risk-analysis-tutorial
–networkVersion 0.0.1 –networkAdmin admin –networkAdminEnrollSecret
adminpw –card [email protected] –file networkadmin.card

然后只需将网络管理员身份导入为可用的业务网卡

composer card import –file networkadmin.card

要测试这是否全部成功,只需ping网络即可

composer network ping –card [email protected]

检查已部署的网络运行的当前版本

composer network ping -c [email protected] | grep Business

Hyperledger Composer可以基于业务网络生成定制的REST API。对于我们的网络,我们现在只想保持简单,所以只需运行以下命令即可创建REST API

composer-rest-server -c [email protected] -n never -u true -w true

现在,您可以通过定位到测试REST API

http://localhost:3000/explorer

您应该看到以下屏幕

Hyperledger Composer REST Server

如果我们要测试我们的CreateNewAsset函数,我们首先需要创建一些保单持有人,所以选择Policyholder,然后选择post并输入以下内容,然后单击try out out

{
“$class”: “org.acme.riskanalysis.Policyholder”,
“id”: “joe”,
“name”: “Joe”,
“balance”: “50000”,
“noClaimsYears”: “2”
}

现在使用以下信息以相同的方式创建另一个保单持有人

{
“$class”: “org.acme.riskanalysis.Policyholder”,
“id”: “mary”,
“name”: “Mary”,
“balance”: “50000”,
“noClaimsYears”: “5”
}

如果不出现错误消息,则对两者的响应应为200,并查看代码是否存在任何潜在错误。

现在我们准备测试CreateNewAsset函数了。要执行此操作,请转到CreateNewAsset> Post。让我们为Joe创造一辆价值2000美元的气车,并寻求12个月的保险。

{
“$class”: “org.acme.riskanalysis.CreateNewAsset”,
“policyholder”: “resource:org.acme.riskanalysis.Policyholder#joe”,
“assetType”: “CAR”,
“value”: 2000,
“durationInMonths”: 12
}

如果一切正常,响应代码应为200。请注意它区分大小写。

现在,如果您转到PrivateAssets> Get,然后单击“try it out”,您应该看到我们刚创建的资产。

现在让我们通过单击终端中的ctrl-c关闭REST服务器,我们将完成其余的事务处理。

RiskAnalysis交易的逻辑只是检查资产类型以及保单持有人没有索赔的年数和资产的价值。

/**
* Risk Analysis
* @param {org.acme.riskanalysis.RiskAnalysis} asset
* @transaction
*/
async function riskAnalysis(asset) {
let assetRegistry = await getAssetRegistry(‘org.acme.riskanalysis.PrivateAsset’);
let score = 0

if (asset.privateAsset.policyholder.noClaimsYears == 1) {
score += 1
}

if (asset.privateAsset.policyholder.noClaimsYears == 2) {
score += 2
}

if (asset.privateAsset.policyholder.noClaimsYears > 2) {
score += 4
}

if (asset.privateAsset.description == ‘Phone’) {
score +=2
}

if (asset.privateAsset.description == ‘House’) {
score +=3
}

if (asset.privateAsset.description == ‘Car’) {
score +=2
}

if (asset.privateAsset.value < 10000.0) {
score += 1
}

if (asset.privateAsset.value < 1000.0) {
score += 1
}

asset.privateAsset.riskAnalysisScore = score

assetRegistry.update(asset.privateAsset)

}

要对此进行测试,请转到package.json文件并将版本号从0.0.1更改为0.0.2,然后创建新的业务网络存档文件

composer archive create –sourceType dir –sourceName . -a [email protected]

安装新文件

composer network install –card [email protected] –archiveFile [email protected]

更新网络

composer network upgrade -c [email protected] -n risk-analysis-tutorial -V 0.0.2

检查网络是否已升级到版本:0.0.2

composer network ping -c [email protected] | grep Business

然后再次运行Composer REST SERVER

composer-rest-server -c [email protected] -n never -u true -w true

您之前创建的所有保单持有人和资产仍将在此处,因此无需再次设置这些保单持有人和资产。 让我们获取之前为Joe添加的汽车的ID。 然后转到RiskAnalysis> Post并输入以下用您的资产ID替换ASSETID。

{
“$class”: “org.acme.riskanalysis.RiskAnalysis”,
“privateAsset”: “resource:org.acme.riskanalysis.PrivateAsset#ASSETID”
}

现在回到PrivateAsset> Get,您应该看到风险分析得分从0变为3。

接下来,让我们实现MakeInsuranceOffer

/**
* Make an insurance offer
* @param {org.acme.riskanalysis.MakeInsuranceOffer} insurance
* @transaction
*/

async function makeInsuranceOffer(insurance) {
let assetRegistry = await getAssetRegistry(‘org.acme.riskanalysis.InsuranceOffer’);

num_id = (Math.floor(Math.random() * ( 999999 – 100000) + 100000)).toString(10)

var factory = getFactory()
var insuranceId = insurance.policyholder.id + ” + num_id
var insuranceOfferAsset = factory.newResource(‘org.acme.riskanalysis’, ‘InsuranceOffer’, insuranceId)
insuranceOfferAsset.policyholder = insurance.policyholder
insuranceOfferAsset.insuranceCompany = insurance.insuranceCompany
insuranceOfferAsset.privateAsset = insurance.privateAsset
insuranceOfferAsset.durationInMonths = insurance.privateAsset.durationInMonths
insuranceOfferAsset.monthlyCost = insurance.monthlyCost

await assetRegistry.add(insuranceOfferAsset)
}

现在让我们添加AcceptInsuranceOffer的功能

/**
* Accepting an insurance offer
* @param {org.acme.riskanalysis.AcceptInsuranceOffer} offer
* @transaction
*/

async function acceptInsuranceOffer(offer) {
let insuranceOfferAssetRegistry = await getAssetRegistry(‘org.acme.riskanalysis.InsuranceOffer’);
let policyholderParticipantRegistry = await getParticipantRegistry(‘org.acme.riskanalysis.Policyholder’);
let privateAssetParticipantRegistry = await getAssetRegistry(‘org.acme.riskanalysis.PrivateAsset’);
let insuranceCompanyParticipantRegistry = await getParticipantRegistry(‘org.acme.riskanalysis.InsuranceCompany’);

var costToDebit = offer.offer.monthlyCost;
let insuranceCompany = “resource:org.acme.riskanalysis.InsuranceCompany#” + offer.offer.insuranceCompany.id;

if (offer.offer.policyholder.balance < costToDebit) {
throw new Error(‘Not enough funds in balance’)
}
offer.offer.policyholder.balance -= costToDebit
offer.offer.insuranceCompany.balance += costToDebit
offer.offer.insuranceCompany.insuranceContracts += 1
offer.offer.status = “accepted”;
offer.offer.privateAsset.insuranceCompany = offer.offer.insuranceCompany;

await insuranceOfferAssetRegistry.update(offer.offer);
await policyholderParticipantRegistry.update(offer.offer.policyholder)
await insuranceCompanyParticipantRegistry.update(offer.offer.insuranceCompany)
await privateAssetParticipantRegistry.update(offer.offer.privateAsset)

}

让我们实现CreateClaim

/**
* Create a claim
* @param {org.acme.riskanalysis.CreateClaim} claim
* @transaction
*/

async function makeClaim(claim) {
let assetResource = “resource:org.acme.riskanalysis.PrivateAsset#” + claim.privateAsset.id;
let assetInsuranceOffer = await query(‘selectInsuranceCompanyByInsuredAsset’, { privateAsset: assetResource });

num_id = (Math.floor(Math.random() * ( 999999 – 100000) + 100000)).toString(10)

let assetRegistry = await getAssetRegistry(‘org.acme.riskanalysis.Claim’);

var factory = getFactory()
var claimId = claim.policyholder.id + ” + num_id
var newClaim = factory.newResource(‘org.acme.riskanalysis’, ‘Claim’, claimId)