区块链系统开发测试----链码部署&开发、系统开发&验证

一.检查配置环境

检查虚拟机环境,确保有正在运行的Hyperledger Fabric区块链,并且其中chaincode_basic、credit_chaincode链码可以正常调用

查看chaincode_basic、credit_chaincode链码调用

二.开发征信链码代码

基于现有征信链码,++开发征信链码的升级版++,使用chaincode-init文件夹中的基础链码模板,创建完善其中lib目录以及index.js内容,在lib中创建CreditPlusContract对象,在对象中添加createCreditSubjectPlus功能,实现征信主体的保存,定义subject变量属性包括(key:征信主体主键, organizationName:征信主体评价机构名, type:评价类型,score:征信积分,creator:创建人,datetime:评价时间),其中属性除score外其余都为string类型。将subject内容上链保存

在链码中添加征信主体查询功能(queryCreditSubjectPlus),要求能够查询所有主体内容(包括:key:征信主体主键, organizationName:征信主体评价机构名, type:评价类型,score:征信积分,creator:创建人,datetime:评价时间)

javascript 复制代码
'use strict';
const { Contract } = require("fabric-contract-api");
class CreditPlusContract extends Contract {
    async createCreditSubjectPlus(ctx, key, organizationName, type, creator, datetime) {
        console.info('=== START:创建征信主体 ===');
        const subject = {
            key: key,
            organizationName: organizationName,
            type: type,
            score: 0,
            creator: creator,
            datetime: datetime
        };
        await ctx.stub.putState(key, Buffer.from(JSON.stringify(subject)));
        console.info('=== END:创建征信主体 ===');
        return subject;
    }
    // 查询征信主体
    async queryCreditSubjectPlus(ctx, subjectKey) {
        console.info('=== START : 查询征信主体 ===');
        const bytes = await ctx.stub.getState(subjectKey);
        if (!bytes || bytes.length === 0) {
            const msg = `${subjectKey} 征信主体不存在`;
            console.warn(msg);
            throw new Error(msg);
        }
        const subject = JSON.parse(bytes.toString());
        console.info('=== END : 查询征信主体 ===');
        return subject;
    }
    
}

module.exports = CreditPlusContract;

在链码结构中test目录中添加对于createCreditSubjectPlus以及QueryCreditSubjectPlus功能的单元测试,提交测试代码和验证结果。

javascript 复制代码
'use strict';
const sinon = require('sinon');
const chai = require('chai');
const sinonChai = require('sinon-chai');
const expect = chai.expect;
chai.use(sinonChai);
let assert = sinon.assert;
const { Context } = require('fabric-contract-api');
const { ChaincodeStub, ClientIdentity } = require('fabric-shim');
const CreditPlusContract = require('../lib/creditContract');
describe('Credit Chaincode Test', () => {
    let stub, ctx, ClientId;

    beforeEach(() => {
        ctx = new Context();
        stub = sinon.createStubInstance(ChaincodeStub);
        stub.getMspID.returns('Org1');
        ctx.setChaincodeStub(stub);

        ClientId = sinon.createStubInstance(ClientIdentity);

        stub.putState.callsFake((key, value) => {
            if (!stub.states) {
                stub.states = {};
            }
            stub.states[key] = value;
        });
        stub.getState.callsFake(async (key) => {
            let ret;
            if (stub.states) {
                ret = stub.states[key];
            }
            return Promise.resolve(ret);
        });
        stub.deleteState.callsFake(async (key) => {
            if (stub.states) {
                delete stub.states[key];
            }
        });
    });
    describe('Test CreditSubject function', () => {
        it('should return success on createCreditSubject', async () => {
            let creditContract = new CreditPlusContract();
            let creditSubject = await creditContract.createCreditSubjectPlus(ctx, "A001", "My Company", "Company","Admin","2024-05-28 14:35:00");
            let scroe = creditSubject.score;
            expect(scroe).to.equals(0);
        });
        it('should return success on queryCreditSubject', async () => {
            let creditContract = new CreditPlusContract();
            await creditContract.createCreditSubjectPlus(ctx, "A001", "My Company", "Company", "Admin", "2024-05-28 14:35:00");
            let creditSubject = await creditContract.queryCreditSubjectPlus(ctx, "A001");
            let name = creditSubject.organizationName;
            expect(name).to.equals("My Company");
        });
    });
})

测试代码:

三.部署征信链码

在虚拟机指定Hyperledger Fabric中实现对应链码的部署并验证部署情况。

将链码名修改切credit_chaincode_plus,删除node_modules目录以及对应package-lock.json文件,上传至服务器chaincode目录下:

打包测试:

javascript 复制代码
export FABRIC_CFG_PATH=${PWD}/config

peer lifecycle chaincode package ./chaincode/credit_chaincode_plus.tar.gz --path ./chaincode/credit_chaincode_plus --lang node --label credit_chaincode_plus_1.0

查看打包结果:

安装链码:运行以下进入fabric-cli容器:

javascript 复制代码
docker exec -it fabric-cli bash
  1. 在org1中安装:运行以下链码安装:
javascript 复制代码
. scripts/set-env.sh 1 0 7051
peer lifecycle chaincode install chaincode/credit_chaincode_plus.tar.gz
  1. 在org2中安装,运行以下链码安装:
javascript 复制代码
. scripts/set-env.sh 2 0 9051
peer lifecycle chaincode install chaincode/credit_chaincode_plus.tar.gz
  1. 查看安装情况
javascript 复制代码
peer lifecycle chaincode queryinstalled

批准链码

org1批准链码

  1. 设置链码环境变量
javascript 复制代码
export CC_PACKAGE_ID=credit_chaincode_plus_1.0:9415a0be8812a91c2e510619a4d2a6a5cd06a8cf8f9cef96ee4eec2f456ab7ec
  1. 设置Org1环境变量
javascript 复制代码
. scripts/set-env.sh 1 0 7051

3.批准链码

javascript 复制代码
peer lifecycle chaincode approveformyorg -o orderer.example.com:7050 --ordererTLSHostnameOverride orderer.example.com --channelID $CHANNEL_NAME --name credit_chaincode_plus --version 1.0 --package-id $CC_PACKAGE_ID --sequence 1 --tls --cafile $ORDERER_CA
  1. 设置Org2环境变量
javascript 复制代码
. scripts/set-env.sh 2 0 9051

2.批准链码

javascript 复制代码
peer lifecycle chaincode approveformyorg -o orderer.example.com:7050 --ordererTLSHostnameOverride orderer.example.com --channelID $CHANNEL_NAME --name credit_chaincode_plus --version 1.0 --package-id $CC_PACKAGE_ID --sequence 1 --tls --cafile $ORDERER_CA

检查提交准备

javascript 复制代码
peer lifecycle chaincode checkcommitreadiness --channelID $CHANNEL_NAME --name credit_chaincode_plus --version 1.0 --sequence 1 --tls --cafile $ORDERER_CA --output json

提交链码

javascript 复制代码
peer lifecycle chaincode commit -o orderer.example.com:7050 --ordererTLSHostnameOverride orderer.example.com --channelID $CHANNEL_NAME --name credit_chaincode_plus --version 1.0 --sequence 1 --tls --cafile $ORDERER_CA --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles $PEER0_ORG1_CA --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles $PEER0_ORG2_CA

查询提交的链码

javascript 复制代码
peer lifecycle chaincode querycommitted --channelID $CHANNEL_NAME --name credit_chaincode_plus --tls --cafile $ORDERER_CA

查看运行镜像形成容器情况

javascript 复制代码
docker logs -f b1ddd21303a5

调用createCreditSubject功能

javascript 复制代码
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_CA --channelID $CHANNEL_NAME --name credit_chaincode_plus --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles $PEER0_ORG1_CA --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles $PEER0_ORG2_CA -c '{"function":"createCreditSubjectPlus", "Args":["A001", "My Company", "Company","Admin","2024-05-28 14:35:00"]}'

调用queryCreditSubject功能

javascript 复制代码
peer chaincode query -C $CHANNEL_NAME --name credit_chaincode_plus -c '{"function":"queryCreditSubjectPlus","Args":["A001"]}'

四.部署删除和更新代码

在链码中添加DeleteCreditSubject,实现按subject变量的key属性删除数据功能

在链码中添加UpdateSubjectScore功能,实现按key更新subject,更新subject变量中的score

javascript 复制代码
'use strict';

const { Contract } = require("fabric-contract-api");

class CreditPlusContract extends Contract {
    async createCreditSubjectPlus(ctx, key, organizationName, type, creator, datetime) {
        console.info('=== START:创建征信主体 ===');
        const subject = {
            key: key,
            organizationName: organizationName,
            type: type,
            score: 0,
            creator: creator,
            datetime: datetime
        };
        await ctx.stub.putState(key, Buffer.from(JSON.stringify(subject)));
        console.info('=== END:创建征信主体 ===');
        return subject;
    }
    // 查询征信主体
    async queryCreditSubjectPlus(ctx, subjectKey) {
        console.info('=== START : 查询征信主体 ===');
        const bytes = await ctx.stub.getState(subjectKey);
        if (!bytes || bytes.length === 0) {
            const msg = `${subjectKey} 征信主体不存在`;
            console.warn(msg);
            throw new Error(msg);
        }
        const subject = JSON.parse(bytes.toString());
        console.info('=== END : 查询征信主体 ===');
        return subject;
    }
    async deleteCreditSubject(ctx, id) {
        const exists = await this.creditSubjectExists(ctx, id);
        if (!exists) {
            throw new Error(`The asset ${id} does not exist`);
        }
        return ctx.stub.deleteState(id);
    }

    async creditSubjectExists(ctx, id) {
        const assetJSON = await ctx.stub.getState(id);
        return assetJSON && assetJSON.length > 0;
    }
    async updateSubjectScore(ctx,subjectKey,inputScore) {
        const exists = await this.creditSubjectExists(ctx,subjectKey);
        if (!exists) {
            throw new Error(`The credit subject ${subjectKey} does not exist`);
        }
        const bytes = await ctx.stub.getState(subjectKey);
        if (!bytes || bytes.length ===0){
            const msg = `${subjectKey} 征信主体不存在`;
            console.warn(msg)
            throw new Error (msg);
        }
        var subject = JSON.parse(bytes.toString());
        subject.score=inputScore;
        return ctx.stub.putState(subjectKey, Buffer.from(JSON.stringify(subject)));
    }
    
}

module.exports = CreditPlusContract;

在链码结构中test目录中添加对于DeleteCreditSubject以及UpdateSubjectScore功能的单元测试

javascript 复制代码
'use strict';
const sinon = require('sinon');
const chai = require('chai');
const sinonChai = require('sinon-chai');
const expect = chai.expect;
chai.use(sinonChai);
let assert = sinon.assert;
const { Context } = require('fabric-contract-api');
const { ChaincodeStub, ClientIdentity } = require('fabric-shim');
const CreditPlusContract = require('../lib/creditContract');
describe('Credit Chaincode Test', () => {
    let stub, ctx, ClientId;

    beforeEach(() => {
        ctx = new Context();
        stub = sinon.createStubInstance(ChaincodeStub);
        stub.getMspID.returns('Org1');
        ctx.setChaincodeStub(stub);

        ClientId = sinon.createStubInstance(ClientIdentity);

        stub.putState.callsFake((key, value) => {
            if (!stub.states) {
                stub.states = {};
            }
            stub.states[key] = value;
        });
        stub.getState.callsFake(async (key) => {
            let ret;
            if (stub.states) {
                ret = stub.states[key];
            }
            return Promise.resolve(ret);
        });
        stub.deleteState.callsFake(async (key) => {
            if (stub.states) {
                delete stub.states[key];
            }
        });
    });
    describe('Test CreditSubject function', () => {
        it('should return success on createCreditSubject', async () => {
            let creditContract = new CreditPlusContract();
            let creditSubject = await creditContract.createCreditSubjectPlus(ctx, "A001", "My Company", "Company","Admin","2024-05-28 14:35:00");
            let scroe = creditSubject.score;
            expect(scroe).to.equals(0);
        });
        it('should return success on queryCreditSubject', async () => {
            let creditContract = new CreditPlusContract();
            await creditContract.createCreditSubjectPlus(ctx, "A001", "My Company", "Company", "Admin", "2024-05-28 14:35:00");
            let creditSubject = await creditContract.queryCreditSubjectPlus(ctx, "A001");
            let name = creditSubject.organizationName;
            expect(name).to.equals("My Company");
        });
        it('should return sucess on DeleteCreditSubject', async () => {
            let creditContract = new CreditPlusContract();
            await creditContract.createCreditSubjectPlus(ctx, "A001", "My Company", "Company", "Admin", "2024-05-28 14:35:00");
            await creditContract.deleteCreditSubject(ctx,"A001");
            let ret = await stub.getState('A001');
            expect(ret).to.equal(undefined);
        });
        it('should return sucess on updateSubjectScore', async () => {
            let creditContract = new CreditPlusContract();
            await creditContract.createCreditSubjectPlus(ctx, "A001", "My Company", "Company", "Admin", "2024-05-28 14:35:00");
            await creditContract.updateSubjectScore(ctx, "A001",10);
            let ret = JSON.parse(await stub.getState('A001'));
            expect(ret.score).to.eql(10);
        });
    });
})

测试代码:

五.更新部署征信链码

在链码中重新部署开发链码,实现链码更新(要求version、sequence有迭代痕迹)

打包测试:

javascript 复制代码
export FABRIC_CFG_PATH=${PWD}/config

peer lifecycle chaincode package ./chaincode/credit_chaincode_plus.tar.gz --path ./chaincode/credit_chaincode_plus --lang node --label credit_chaincode_plus_1.1

安装链码

运行以下进入fabric-cli容器:

javascript 复制代码
docker exec -it fabric-cli bash
  1. 在org1中安装 | 运行以下链码安装:
javascript 复制代码
. scripts/set-env.sh 1 0 7051
javascript 复制代码
peer lifecycle chaincode install chaincode/credit_chaincode_plus.tar.gz
  1. 在org2中安装

运行以下链码安装:

javascript 复制代码
. scripts/set-env.sh 2 0 9051
javascript 复制代码
peer lifecycle chaincode install chaincode/credit_chaincode_plus.tar.gz
  1. 查看安装情况
javascript 复制代码
peer lifecycle chaincode queryinstalled

三.批准链码

org1批准链码

设置链码环境变量

javascript 复制代码
export CC_PACKAGE_ID=credit_chaincode_plus_1.1:a7d65de21f706c25029b84cbbf7de2163d06d9b8e9fc33fd20a5a59cc3a18b01
  1. 设置Org1环境变量
javascript 复制代码
. scripts/set-env.sh 1 0 7051

3.批准链码

javascript 复制代码
peer lifecycle chaincode approveformyorg -o orderer.example.com:7050 --ordererTLSHostnameOverride orderer.example.com --channelID $CHANNEL_NAME --name credit_chaincode_plus --version 1.1 --package-id $CC_PACKAGE_ID --sequence 2 --tls --cafile $ORDERER_CA
  1. 设置Org2环境变量
javascript 复制代码
. scripts/set-env.sh 2 0 9051

2.批准链码

javascript 复制代码
peer lifecycle chaincode approveformyorg -o orderer.example.com:7050 --ordererTLSHostnameOverride orderer.example.com --channelID $CHANNEL_NAME --name credit_chaincode_plus --version 1.1 --package-id $CC_PACKAGE_ID --sequence 2 --tls --cafile $ORDERER_CA

检查提交准备

javascript 复制代码
peer lifecycle chaincode checkcommitreadiness --channelID $CHANNEL_NAME --name credit_chaincode_plus --version 1.1 --sequence 2 --tls --cafile $ORDERER_CA --output json

提交链码

javascript 复制代码
peer lifecycle chaincode commit -o orderer.example.com:7050 --ordererTLSHostnameOverride orderer.example.com --channelID $CHANNEL_NAME --name credit_chaincode_plus --version 1.1 --sequence 2 --tls --cafile $ORDERER_CA --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles $PEER0_ORG1_CA --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles $PEER0_ORG2_CA

查询提交的链码

javascript 复制代码
peer lifecycle chaincode querycommitted --channelID $CHANNEL_NAME --name credit_chaincode_plus --tls --cafile $ORDERER_CA

查看运行镜像形成容器情况

javascript 复制代码
docker logs -f b1ddd21303a5

调用createCreditSubject功能

javascript 复制代码
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_CA --channelID $CHANNEL_NAME --name credit_chaincode_plus --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles $PEER0_ORG1_CA --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles $PEER0_ORG2_CA -c '{"function":"createCreditSubjectPlus", "Args":["A001", "My Company", "Company","Admin","2024-05-28 14:35:00"]}'

调用queryCreditSubject功能

javascript 复制代码
peer chaincode query -C $CHANNEL_NAME --name credit_chaincode_plus -c '{"function":"queryCreditSubjectPlus","Args":["A001"]}'

调用deleteCreditSubject功能

javascript 复制代码
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_CA --channelID $CHANNEL_NAME --name credit_chaincode_plus --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles $PEER0_ORG1_CA --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles $PEER0_ORG2_CA -c '{"function":"deleteCreditSubject", "Args":["A001"]}'

调用updateSubjectScore功能(这边如果嫌麻烦的话可以更新功能之后再调用删除功能)

javascript 复制代码
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_CA --channelID $CHANNEL_NAME --name credit_chaincode_plus --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles $PEER0_ORG1_CA --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles $PEER0_ORG2_CA -c '{"function":"updateSubjectScore", "Args":["A002","10"]}'
相关推荐
Lary_Rock1 小时前
RK3576 LINUX RKNN SDK 测试
linux·运维·服务器
云飞云共享云桌面3 小时前
8位机械工程师如何共享一台图形工作站算力?
linux·服务器·网络
Peter_chq4 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
一坨阿亮5 小时前
Linux 使用中的问题
linux·运维
dsywws6 小时前
Linux学习笔记之vim入门
linux·笔记·学习
幺零九零零7 小时前
【C++】socket套接字编程
linux·服务器·网络·c++
小林熬夜学编程8 小时前
【Linux系统编程】第四十一弹---线程深度解析:从地址空间到多线程实践
linux·c语言·开发语言·c++·算法
程思扬9 小时前
为什么Uptime+Kuma本地部署与远程使用是网站监控新选择?
linux·服务器·网络·经验分享·后端·网络协议·1024程序员节
sun0077009 小时前
拷贝 cp -rdp 和 cp -a
linux·运维·服务器
wowocpp9 小时前
ubuntu 22.04 server 安装 anaconda3
linux·运维·ubuntu