草庐IT

【Hyperledger-fabric入门学习记录】Fabcar实例

孟小橘 2024-03-26 原文

【Hyperledger-fabric入门学习记录】Fabcar

实验环境

实验目标

  • 编写一个应用程序和智能合约来查询和更新一个分类账
  • 使用证书颁发机构生成X.509证书,这些证书由与受许可的区块链交互的应用程序使用

应用工具

应用SDK(application SDK)——调用智能合约SDK(smart contract SDK)查询和更新账本

步骤

搭建环境

关闭其他网络

  • 实验前需要关闭其他运行的fabric的测试程序中的容器和网络
    一定要关闭其他容器,不然会报错无法运行。
docker rm -f $(docker ps -aq)
docker rmi -f $(docker images | grep fabcar | awk '{print $3}')

  • 检查正在运行的容器
docker ps -a
  • 显示已经没有容器

实验过程

  • 进入~/go/src/github.com/hyperledger/fabric-samples/fabcar/目录
  • 启动网络
./startFabric.sh

  • 进入javascript目录
cd javascript
  • 在此文件夹内安装npm
    只需在首次运行时安装,之后可以跳过
    npm使应用程序能够使用身份、钱包和网关连接到通道、提交事务和等待通知。
\\官方指令
sudo npm install

我在安装的时候遇到“找不到指令”的错误:

使用这两条指令

sudo apt-get install npm
sudo npm install

安装成功,文件夹内出现node_modules

  • 创建admin
    当我们创建网络时,需要一个admin用户(简称为admin),作为证书颁发机构(CA)的注册者。
    第一步是使用enroll.js程序为admin生成私钥、公钥和X.509证书。
    这个过程使用证书签名请求(CSR):私钥和公钥首先在本地生成,然后将公钥发送到CA, CA返回一个编码的证书供应用程序使用。
    将这三个凭据存储在钱包中,允许我们充当CA的管理员。
  • 步骤:
node enrollAdmin.js
  • 创建成功


该命令将CA管理员的凭据存储在钱包目录中。

  • 创建user
    现在在钱包中已经有了管理员的凭证,我们可以注册一个新用户user1,它将用于查询和更新分类帐:
node registerUser.js

与管理员注册类似,该程序使用CSR注册user1,并将其凭据与管理员的凭据一起存储在钱包中。现在,我们有了两个独立用户的身份——admin和user1,可以被应用程序使用。

  • 访问账本
    区块链网络中的每个peer都掌握分类帐的一个副本,应用程序可以通过调用智能合约查询分类帐的最新值并将其返回给应用程序,从而查询分类帐。
    下面是一个查询的工作示意图:

    应用程序使用查询从分类帐中读取数据。最常见的查询涉及分类帐中数据的当前值即世界状态(world state)。世界状态表示为一组键-值对,应用程序可以查询单个键或多个键的数据。此外,可以配置分类帐世界状态以便于使用CouchDB这样的数据库,当键值被建模为JSON数据时,CouchDB支持复杂的查询。这在查找与特定值的特定关键字匹配的所有资产时非常有用,例如,所有拥有特定车主的汽车。
    首先,运行query.js程序,返回账本上所有汽车的列表。
  • 使用身份user1来访问分类帐:
node query.js

此时返回所有车辆的信息。

接下来详细分析这个过程:
打开node.js文件,可以看到应用程序首先从fabric-network模块引入scope两个关键类:FileSystemWallet和gateway(网关)。这些类将用于定位钱包中的user1身份,并使用它连接到网络:

const { FileSystemWallet, Gateway } = require('fabric-network');

应用程序使用网关连接到网络:

const gateway = new Gateway();
await gateway.connect(ccp, { wallet, identity: 'user1' });

创建一个新的网关,然后使用它将应用程序连接到网络。ccp描述网关将使用来自钱包的身份user1访问的网络。
…/…/first-network/ connectionorg1记录了如何加载ccp.json并解析为json文件:

const ccpPath = path.resolve(__dirname, '..', '..', 'first-network', 'connection-org1.json');
const ccpJSON = fs.readFileSync(ccpPath, 'utf8');
const ccp = JSON.parse(ccpJSON);

网络可以划分为多个通道,将应用程序连接到网络中的一个特定通道mychannel。在这个通道中,我们可以访问智能合约fabcar与账本交互:

const contract = network.getContract('fabcar');

fabcar中有许多不同的事务,我们的应用程序先使用queryAllCars事务来访问分类帐世界状态数据:

const result = await contract.evaluateTransaction('queryAllCars');

其中,evaluateTransaction是区块链网络中与智能合约最简单的交互之一。它只是选择一个在连接配置文件中定义的peer,并将请求发送给它,在那里对请求进行评估。智能合约查询同行的账本副本上的所有汽车,并将结果返回给应用程序。这种交互不会导致账簿的更新。

  • Fabcar中的智能合约
    打开fabric-samples根目录下的chaincode/fabcar/javascript/lib子目录中的fabcar.js。
    使用contract类定义智能合约:
class FabCar extends Contract {...


在这个类结构中,可以看到initLedger、queryCar、queryAllCars、createCar和changeCarOwner的结构定义。

    async queryCar(ctx, carNumber) {
        const carAsBytes = await ctx.stub.getState(carNumber); //从chaincode state中获取车辆信息
        if (!carAsBytes || carAsBytes.length === 0) {
            throw new Error(`${carNumber} does not exist`);
        }
        console.log(carAsBytes.toString());
        return carAsBytes.toString();
    }
    //向区块链添加一个区块
    async createCar(ctx, carNumber, make, model, color, owner) {
        console.info('============= START : Create Car ===========');
        const car = {
            color,
            docType: 'car',
            make,
            model,
            owner,
        };
        await ctx.stub.putState(carNumber, Buffer.from(JSON.stringify(car)));
        console.info('============= END : Create Car ===========');
    }
    //更新分类账
    async queryAllCars(ctx) {
        const startKey = 'CAR0';//定义车辆编号范围为0-999
        const endKey = 'CAR999';
        const iterator = await ctx.stub.getStateByRange(startKey, endKey);
        const allResults = [];
        //遍历查询结果并将它们打包为应用程序的JSON。
        while (true) {
            const res = await iterator.next();
            if (res.value && res.value.value.toString()) {
                console.log(res.value.value.toString('utf8'));
                const Key = res.value.key;
                let Record;
                try {
                    Record = JSON.parse(res.value.value.toString('utf8'));
                } catch (err) {
                    console.log(err);
                    Record = res.value.value.toString('utf8');
                }
                allResults.push({ Key, Record });
            }
            if (res.done) {
                console.log('end of data');
                await iterator.close();
                console.info(allResults);
                return JSON.stringify(allResults);
            }
        }
    }
    async changeCarOwner(ctx, carNumber, newOwner) {
        console.info('============= START : changeCarOwner ===========');
        const carAsBytes = await ctx.stub.getState(carNumber); // get the car from chaincode state
        if (!carAsBytes || carAsBytes.length === 0) {
            throw new Error(`${carNumber} does not exist`);
        }
        const car = JSON.parse(carAsBytes.toString());
        car.owner = newOwner;
        await ctx.stub.putState(carNumber, Buffer.from(JSON.stringify(car)));
        console.info('============= END : changeCarOwner ===========');
    }
}

下面是应用程序如何在智能合约中调用不同事务的示意图示。每个事务使用一组广泛的api(如getStateByRange)与分类帐进行交互。

当我们打开query.js文件修改evaluatorTransaction中的请求,可以查询特定车辆的信息
原始请求:

修改后的请求:

  • 修改后只能查询到特定车辆的信息

遇到的问题

第一天运行结束能够查看全部车辆信息后关闭了网络、容器还有虚拟机。
第二天想要修改query.js中的请求实现只查询特定信息。按照步骤,启动网络,创建管理员和用户身份。
创建的过程中,指令的结果出现了 “An identity for the admin user “admin” already exists in the wallet"和"An identity for the user “user1” already exists in the wallet.”,但是没有在意。
query命令执行之后出现错误,无法访问信息:

mxj@mxj-VirtualBox:~/go/src/github.com/hyperledger/fabric-samples/fabcar/javascript$ node query.js

Wallet path: /home/mxj/go/src/github.com/hyperledger/fabric-samples/fabcar/javascript/wallet

2022-10-28T13:51:52.325Z - error: [Channel.js]: Channel:mychannel received discovery error:access denied

2022-10-28T13:51:52.327Z - error: [Channel.js]: Error: Channel:mychannel Discovery error:access denied

2022-10-28T13:51:52.356Z - error: [Channel.js]: Channel:mychannel received discovery error:access denied

2022-10-28T13:51:52.357Z - error: [Channel.js]: Error: Channel:mychannel Discovery error:access denied

2022-10-28T13:51:52.360Z - error: [Network]: _initializeInternalChannel: Unable to initialize channel. Attempted to contact 2 Peers. Last error was Error: Channel:mychannel Discovery error:access denied

    at Channel._discover (/home/mxj/go/src/github.com/hyperledger/fabric-samples/fabcar/javascript/node_modules/fabric-client/lib/Channel.js:1254:11)

    at async Channel._initialize (/home/mxj/go/src/github.com/hyperledger/fabric-samples/fabcar/javascript/node_modules/fabric-client/lib/Channel.js:282:32)

    at async Channel.initialize (/home/mxj/go/src/github.com/hyperledger/fabric-samples/fabcar/javascript/node_modules/fabric-client/lib/Channel.js:235:14)

    at async Network._initializeInternalChannel (/home/mxj/go/src/github.com/hyperledger/fabric-samples/fabcar/javascript/node_modules/fabric-network/lib/network.js:112:5)

    at async Network._initialize (/home/mxj/go/src/github.com/hyperledger/fabric-samples/fabcar/javascript/node_modules/fabric-network/lib/network.js:137:3)

    at async Gateway.getNetwork (/home/mxj/go/src/github.com/hyperledger/fabric-samples/fabcar/javascript/node_modules/fabric-network/lib/gateway.js:293:3)

    at async main (/home/mxj/go/src/github.com/hyperledger/fabric-samples/fabcar/javascript/query.js:33:25)

Failed to evaluate transaction: Error: Unable to initialize channel. Attempted to contact 2 Peers. Last error was Error: Channel:mychannel Discovery error:access denied

    at Channel._discover (/home/mxj/go/src/github.com/hyperledger/fabric-samples/fabcar/javascript/node_modules/fabric-client/lib/Channel.js:1254:11)

    at async Channel._initialize (/home/mxj/go/src/github.com/hyperledger/fabric-samples/fabcar/javascript/node_modules/fabric-client/lib/Channel.js:282:32)

    at async Channel.initialize (/home/mxj/go/src/github.com/hyperledger/fabric-samples/fabcar/javascript/node_modules/fabric-client/lib/Channel.js:235:14)

    at async Network._initializeInternalChannel (/home/mxj/go/src/github.com/hyperledger/fabric-samples/fabcar/javascript/node_modules/fabric-network/lib/network.js:112:5)

    at async Network._initialize (/home/mxj/go/src/github.com/hyperledger/fabric-samples/fabcar/javascript/node_modules/fabric-network/lib/network.js:137:3)

    at async Gateway.getNetwork (/home/mxj/go/src/github.com/hyperledger/fabric-samples/fabcar/javascript/node_modules/fabric-network/lib/gateway.js:293:3)


百度之后并没有查到完全一致的问题和解决方案,但是发现了存在已经创建身份不会因为容器和网络关闭被删除的问题。
于是重启网络和容器,直接执行 node query.js,依旧报错。
最后删除了/javascript/wallet下的admin和user1文件夹,再次创建admin和user身份,请求成功。
收获经验:每次运行网络要把之前创建的身份都删除才能实现本次网络中的请求,不然身份不对应无法实现操作。

  • 更新账本:创建一辆新车
    应用程序向区块链网络提交事务,当该事务经过验证和提交后,应用程序将收到事务成功的通知。
    这涉及到达成共识的过程,区块链网络的不同组成部分一起工作,以确保对分类帐的每一次更新都是有效的,并以商定的和一致的顺序执行。
    invoke.js的程序可以更新分类帐。
    构建事务的代码块,可以将信息提交给网络:
await contract.submitTransaction('createCar', 'CAR12', 'Honda', 'Accord', 'Black', 'Tom');

调用应用程序使用submitTransaction API而不是evaluateTransaction与区块链网络交互。

submitTransaction比evaluateTransaction复杂得多。SDK不是与单个对等点交互,而是将submitTransaction提议发送到区块链网络中每个所需组织的peer。每个peer将使用此提议执行所请求的智能合约,以生成事务响应,并签署该事务响应并返回给SDK。SDK将所有签名的事务响应收集到一个事务中,然后将该事务发送给orderer。orderer从每个应用程序收集事务并将其排序到一个事务块中。然后,它将这些块分发给网络中的每个对等体,在那里每个事务都得到验证和提交。最后,通知SDK,允许它将控制返回给应用程序。

node

  • 运行invoke.js
node invoke.js

  • 再次访问账本,出现一辆新的车
  • 修改query.js中的请求,查询只查询新的车:
    node.js中修改为:

    查询结果:
  • 修改账本中的信息
    首先修改invoke.js中的函数,将createCar修改为changeCarOwner :
await contract.submitTransaction('changeCarOwner', 'CAR12', 'Dave');

保存后再次执行invoke.js和query.js

node invoke.js
node query.js

查询到CAR12的拥有者已经从Tom变更为Dave。

至此,Fabcar中的基本实验操作均已完成。

  • 关闭所有网络和容器:
docker rm -f $(docker ps -aq)
docker rmi -f $(docker images | grep fabcar | awk '{print $3}')

参考文献

1.官方文档 https://hyperledger-fabric.readthedocs.io/en/release-1.4/write_first_app.html
2.https://www.cnblogs.com/HeartKing/p/14852536.html

有关【Hyperledger-fabric入门学习记录】Fabcar实例的更多相关文章

  1. ruby-on-rails - 如何使用 instance_variable_set 正确设置实例变量? - 2

    我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击

  2. ruby 正则表达式 - 如何替换字符串中匹配项的第 n 个实例 - 2

    在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg

  3. ruby - Sinatra:运行 rspec 测试时记录噪音 - 2

    Sinatra新手;我正在运行一些rspec测试,但在日志中收到了一堆不需要的噪音。如何消除日志中过多的噪音?我仔细检查了环境是否设置为:test,这意味着记录器级别应设置为WARN而不是DEBUG。spec_helper:require"./app"require"sinatra"require"rspec"require"rack/test"require"database_cleaner"require"factory_girl"set:environment,:testFactoryGirl.definition_file_paths=%w{./factories./test/

  4. ruby-on-rails - Rails - 从另一个模型中创建一个模型的实例 - 2

    我有一个正在构建的应用程序,我需要一个模型来创建另一个模型的实例。我希望每辆车都有4个轮胎。汽车模型classCar轮胎模型classTire但是,在make_tires内部有一个错误,如果我为Tire尝试它,则没有用于创建或新建的activerecord方法。当我检查轮胎时,它没有这些方法。我该如何补救?错误是这样的:未定义的方法'create'forActiveRecord::AttributeMethods::Serialization::Tire::Module我测试了两个环境:测试和开发,它们都因相同的错误而失败。 最佳答案

  5. ruby-on-rails - Rails 5 Active Record 记录无效错误 - 2

    我有两个Rails模型,即Invoice和Invoice_details。一个Invoice_details属于Invoice,一个Invoice有多个Invoice_details。我无法使用accepts_nested_attributes_forinInvoice通过Invoice模型保存Invoice_details。我收到以下错误:(0.2ms)BEGIN(0.2ms)ROLLBACKCompleted422UnprocessableEntityin25ms(ActiveRecord:4.0ms)ActiveRecord::RecordInvalid(Validationfa

  6. ruby-on-rails - RSpec:避免使用允许接收的任何实例 - 2

    我正在处理旧代码的一部分。beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)endRubocop错误如下:Avoidstubbingusing'allow_any_instance_of'我读到了RuboCop::RSpec:AnyInstance我试着像下面那样改变它。由此beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)end对此:let(:sport_

  7. ruby-on-rails - 使用 ruby​​ 将多个实例变量转换为散列的更好方法? - 2

    我收到格式为的回复#我需要将其转换为哈希值(针对活跃商家)。目前我正在遍历变量并执行此操作:response.instance_variables.eachdo|r|my_hash.merge!(r.to_s.delete("@").intern=>response.instance_eval(r.to_s.delete("@")))end这有效,它将生成{:first="charlie",:last=>"kelly"},但它似乎有点hacky和不稳定。有更好的方法吗?编辑:我刚刚意识到我可以使用instance_variable_get作为该等式的第二部分,但这仍然是主要问题。

  8. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  9. CAN协议的学习与理解 - 2

    最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总

  10. 深度学习部署:Windows安装pycocotools报错解决方法 - 2

    深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal

随机推荐