Fabric官⽅教程(release2.2)翻译及总结——编写属于你的第⼀个APP
编写属于你的第⼀个APP
Set up the blockchain network
Launch the network
./network.sh down
./network.sh up createChannel -c mychannel -ca
注意该指令需要分成两条指令来执⾏
[外链图⽚转存失败,源站可能有防盗链机制,建议将图⽚保存下来直接上传(img-y6QhPlML-1634268920958)
(file://C:/Users/62483/AppData/Roaming/Typora/typora-user-images/image-20210513113241530.png?
lastModify=1621323966)]
如果您决定通过断开⽹络并重新进⾏备份来重新开始,则必须在重新运⾏javascript应⽤程序之前删除wallet⽂件夹及其标识,否则会出现错误。 发⽣这种情况的原因是,在关闭测试⽹络时,关闭了证书颁发机构及其数据库,但是原始钱包仍保留在application-javascript⽬录中,因此必须将其删除。 当您重新运⾏⽰例javascript应⽤程序时,将⽣成⼀个新的钱包和凭据。
此命令将部署具有两个peer⽅,⼀个排序服务和三个证书颁发机构(Orderer,Org1,Org2)的Fabric测试⽹络。 代替使⽤cryptogen ⼯具,我们使⽤证书颁发机构(因此使⽤-ca标志)启动测试⽹络。 此外,启动证书颁发机构时会引导组织管理员⽤户注册。 在后续步骤中,我们将显⽰⽰例应⽤程序如何完成管理员注册。 接下来,通过调⽤带有链码名称和语⾔选项的./network.sh脚本来部署链码。
./network.sh deployCC -ccn basic -ccp ../asset-transfer-basic/chaincode-javascript/ -ccl javascript
注意:在后台,此脚本使⽤链码⽣命周期来打包,安装,查询已安装的链码,批准Org1和Org2的链码,最后提交链码。
Sample application
打开新的终端,进⼊application-javascript⽬录:
cd asset-transfer-basic/application-javascript
该⽬录包含使⽤Fabric SDK for Node.js开发的⽰例程序。 运⾏以下命令以安装应⽤程序依赖项。 最多可能需要⼀分钟才能完成:
npm install
此过程将安装在应⽤程序的package.json中定义的关键应⽤程序依赖项。 其中最重要的是结构⽹络Node.js模块。 它使应⽤程序能够使⽤⾝份,钱包和⽹关来连接到通道,提交交易并等待通知。 本教程还使⽤fabric-ca-client模块使⽤各⾃的证书颁发机构来注册⽤户,⽣成有效的⾝份,然后由fabric-network模块使⽤该⾝份与区块链⽹络进⾏交互。
npm安装完成后,⼀切就绪,可以运⾏该应⽤程序。 让我们看⼀下将在本教程中使⽤的⽰例JavaScript应⽤程序⽂件。 运⾏以下命令以列出此⽬录中的⽂件:
ls
可以看到有如下相关⽂件:
app.js                  node_modules            package.json      package-lock.json
注意:下⼀节的第⼀部分涉及与证书颁发机构的通信。 您可能会发现通过运⾏新的终端外壳并运⾏docker logs -f ca_org1在运⾏即将到来的程序时流式传输CA⽇志很有⽤。
第⼀步,当我们启动Fabric测试⽹络时,创建了⼀个管理员⽤户(字⾯上称为admin)作为证书颁发机构(CA)的注册商。 我们的第⼀步是通过让应⽤程序调⽤enrollAdmin来⽣成admin的私钥,公钥和X.509证书。 此过程使⽤证书签名请求(CSR)-专⽤密钥和公⽤密钥⾸先在本地⽣成,然后将公⽤密钥发送到CA,CA返回编码的证书供应⽤程序使⽤。 这些凭证然后存储在钱包中,使我们能够充当CA的管理员。
让我们运⾏该应⽤程序,然后逐步完成与智能合约功能的每种交互。 在asset-transfer-basic / application-javascript⽬录中,运⾏以下命令:
node app.js
First, the application enrolls the admin user
第⼀步,当我们启动Fabric测试⽹络时,创建了⼀个管理员⽤户(字⾯上称为admin)作为证书颁发机构(CA)的注册商。 我们的第⼀步是通过让应⽤程序调⽤enrollAdmin来⽣成admin的私钥,公钥和X.509证书。 此过程使⽤证书签名请求(CSR)-⾸先在本地⽣成私钥和公钥,然后将公钥发送到CA,CA返回编码的证书以供应⽤程序使⽤。 这些凭证然后存储在钱包中,使我们能够充当CA的管理员。
在下⾯的⽰例应⽤程序代码中,您将看到在参考公共连接配置⽂件路径之后,确保连接配置⽂件存在,并指定创建钱包的位置,然后执⾏enrollAdmin()并从证书⽣成管理凭据。 权威。
async function main() {
try {
// build an in memory object with the network configuration (also known as a connection profile)
const ccp = buildCCP();
// build an instance of the fabric ca services client based on
// the information in the network configuration
const caClient = buildCAClient(FabricCAServices, ccp);
// setup the wallet to hold the credentials of the application user
const wallet = await buildWallet(Wallets, walletPath);
// in a real application this would be done on an administrative flow, and only once
await enrollAdmin(caClient, wallet);
此命令将CA管理员的凭据存储在wallet⽬录中。 您可以在钱包中到管理员的证书和私钥 。
对于应⽤程序⽤户,我们需要应⽤程序在下⼀步中注册和注册⽤户。
Second, the application registers and enrolls an application user
现在,我们已经在钱包中拥有管理员的凭据,该应⽤程序将使⽤admin⽤户注册并注册⼀个将与区块链⽹络进⾏交互的应⽤程序⽤户。 应⽤程序代码部分如下所⽰。
// in a real application this would be done only when a new user was required to be added
// and would be part of an administrative flow
await registerUser(caClient, wallet, userId, 'org1.department1');
与管理员注册类似,此功能使⽤CSR来注册和注册appUser并将其凭据与admin的凭据⼀起存储在钱包中。 现在,我们有两个独⽴⽤户的⾝份-admin和appUser-可以由我们的应⽤程序使⽤。
[外链图⽚转存失败,源站可能有防盗链机制,建议将图⽚保存下来直接上传(img-fpfAVUOc-1634268
920959)
(file://C:/Users/62483/AppData/Roaming/Typora/typora-user-images/image-20210513150922817.png?
lastModify=1621323966)]
Third, the sample application prepares a connection to the channel and smart contract
在前⾯的步骤中,应⽤程序⽣成了管理员和应⽤程序⽤户凭据,并将其放置在钱包中。 如果凭据存在并且具有与之关联的正确权限属性,则⽰例应⽤程序⽤户将能够在引⽤通道名称和合同名称之后调⽤链码功能。
我们的连接配置仅指定您⾃⼰的组织中的peer。 我们告诉节点客户端sdk使⽤服务发现(在同级上运⾏),该服务获取当前处于联机状态的其他同级,元数据(如相关的认可策略)以及任何其拥有的静态信息,否则需要与其余节点进⾏通信。 设置为true的asLocalhost告诉它以localhost连接,因为我们的客户端与其他结构节点在同⼀⽹络上运⾏。 在不与其他光纤⽹络节点在同⼀⽹络上运⾏客户端的部署
中,asLocalhost选项将设置为false。
您会注意到,在以下应⽤程序代码⾏中,应⽤程序正在通过⽹关使⽤合同名称和信道名称来引⽤合同:
// Create a new gateway instance for interacting with the fabric network.
// In a real application this would be done as the backend server session is setup for
// a user that has been verified.
const gateway = new Gateway();
try {
// setup the gateway instance
// The user will now be able to create connections to the fabric network and be able to
// submit transactions and query. All transactions submitted by this gateway will be
// signed by this user using the credentials stored in the wallet.
t(ccp, {
wallet,
identity: userId,
discovery: {enabled: true, asLocalhost: true} // using asLocalhost as this gateway is using a fabric network deployed locally
});
// Build a network instance based on the channel where the smart contract is deployed
const network = Network(channelName);
// Get the contract from the network.
const contract = Contract(chaincodeName);
当链码包包含多个智能合约时,可以在getContract()API上指定链码包的名称和要定位的特定智能合约。 例如:
const contract = Contract('chaincodeName', 'smartContractName');
Fourth, the application initializes the ledger with some sample data
现在我们已经到了实际让⽰例应⽤程序提交交易的地步,让我们按顺序进⾏处理。 为每个被调⽤的函数以及终端输出提供了应⽤程序代码⽚段和调⽤的链代码⽚段。
SubmitTransaction()函数⽤于调⽤链码InitLedger函数,以使⽤⼀些样本数据填充账本。 在幕后,submitTransaction()函数将使⽤服务发现来为链码到⼀组所需的认可peer体,在所需数⽬的peer体上调⽤链码,从这些peer体收集链码认可的结果,最后将交易提交给排序服务。
⽰例app调⽤’InitLedger’:
// Initialize a set of asset data on the channel using the chaincode 'InitLedger' function.
// This type of transaction would only be run once by an application the first time it was started after it
// deployed the first time. Any updates to the chaincode deployed later would likely not need to run
// an "init" type function.
console.log('\n--> Submit Transaction: InitLedger, function creates the initial set of assets on the ledger');
await contract.submitTransaction('InitLedger');
console.log('*** Result: committed');
'InitLedger’链码:
图片编辑器appasync InitLedger(ctx) {
const assets = [
{
ID: 'asset1',
Color: 'blue',
Size: 5,
Owner: 'Tomoko',
AppraisedValue: 300,
},
{
ID: 'asset2',
Color: 'red',
Size: 5,
Owner: 'Brad',
AppraisedValue: 400,
},
{
ID: 'asset3',
Color: 'green',
Size: 10,
Owner: 'Jin Soo',
AppraisedValue: 500,
},
{
ID: 'asset4',
Color: 'yellow',
Size: 10,
Owner: 'Max',
AppraisedValue: 600,
},
{
ID: 'asset5',
Color: 'black',
Size: 15,
Owner: 'Adriana',
AppraisedValue: 700,
},
{
ID: 'asset6',
Color: 'white',
Size: 15,
Owner: 'Michel',
AppraisedValue: 800,
},
];
for (const asset of assets) {
asset.docType = 'asset';
await ctx.stub.putState(asset.ID, Buffer.from(JSON.stringify(asset)));
console.info(`Asset ${asset.ID} initialized`);
}
}
Fifth, the application invokes each of the chaincode functions
⾸先,关于查询账本的⼀句话。 区块链⽹络中的每个peer都托管账本的副本。 应⽤程序可以使⽤peer上运⾏的智能合约的只读调⽤(称为查询)来查看账本中的最新数据。 这是查询⼯作⽅式的简化表⽰:
最常见的查询涉及账本中数据的当前值-其世界状态。 世界状态表⽰为⼀组键/值对,应⽤程序可以查
询单个键或多个键的数据。 此外,当您使⽤CouchDB作为状态数据库并使⽤JSON建模数据时,可以使⽤复杂的查询来读取账本上的数据。 当寻与某些关键字具有特定值匹配的所有资产时,这将⾮常有⽤。 例如,具有特定所有者的所有资产。 下⾯,该⽰例应⽤程序仅获取我们在使⽤数据初始化账本时在上⼀步中填充的所有资产。 如果您要查询单个peer,⽽⽆需向定单服务提交交易,则可以使⽤validateTransaction()函数。
在序列的下⼀部分中,⽰例应⽤程序进⾏评估以查看是否存在asset1,这将返回布尔值true,因为在⽤资产初始化账本时,我们⽤asset1填充了账本。 您可能还记得资产1的原始评估值为300。应⽤程序随后提交了⼀个交易,以⽤新的评估值更新资产1,然后⽴即进⾏评估以从账本中读取资产1,以显⽰新的评估值350。⽰例应⽤程序’ AssetExists”,“ UpdateAsset”和“ ReadAsset”调⽤
A closer look
让我们仔细研究⼀下⽰例javascript应⽤程序如何使⽤Fabric Node SDK提供的API与我们的Fabric⽹络进⾏交互。 使⽤编辑器(例如atom或visual studio)打开位于资产转移基础中的app.js
该应⽤程序⾸先从fabric-network模块中引⼊作⽤域两个关键类:wallet和gateway。 这些类将⽤于在钱包中到appUser⾝份,并使⽤它来连接到⽹络:
const { Gateway, Wallets } = require('fabric-network');
⾸先,该程序使⽤存储在钱包中的userId设置⽹关连接,并指定发现选项。
// setup the gateway instance
// The user will now be able to create connections to the fabric network and be able to
// submit transactions and query. All transactions submitted by this gateway will be
// signed by this user using the credentials stored in the wallet.
t(ccp, {
wallet,
identity: userId,
discovery: {enabled: true, asLocalhost: true} // using asLocalhost as this gateway is using a fabric network deployed locally
});
请注意,在⽰例应⽤程序代码的顶部,我们需要外部实⽤程序⽂件来构建CAClient,registerUser,enrollAdmin,buildCCP(公共连接配置⽂件)和buildWallet。 这些实⽤程序位于测试应⽤程序的AppUtil.js中 ,在test-application/javascript⽬录下。
在AppUtil.js中,ccpPath描述了应⽤程序⽤来连接到我们的⽹络的连接配置⽂件的路径。 连接配置⽂件是从fabric-samples/test-network⽬录中加载并被解释为JSON⽂件。
如果您想进⼀步了解连接配置⽂件的结构以及它如何定义⽹络,请查看连接配置⽂件主题。