Hyperledger Fabric v1.4(LTS) 系列(6.1):Writing Your First Application

Hyperledger Fabric v1.4(LTS) 系列(6.1):Writing Your First Application

If you’re not yet familiar with the fundamental architecture of a Fabric network, you may want to visit the Key Concepts section prior to continuing.
It is also worth noting that this tutorial serves as an introduction to Fabric applications and uses simple smart contracts and applications. For a more in-depth look at Fabric applications and smart contracts, check out our
Developing Applications section or the Commercial Paper .

In this tutorial we’ll be looking at a handful of sample programs to see how Fabric apps work. These applications and the smart contracts they use are collectively known as FabCar. They provide a great starting point to understand a Hyperledger Fabric blockchain. You’ll learn how to write an application and smart contract to query and update a ledger, and how to use a Certificate Authority to generate the X.509 certificates used by applications which interact with a permissioned blockchain.

We will use the application SDK — described in detail in the Application topic – to invoke a smart contract which queries and updates the ledger using the smart contract SDK — described in detail in section Smart Contract Processing

We’ll go through three principle steps:

1. Setting up a development environment. Our application needs a network to interact with, so we’ll get a basic network our smart contracts and application will use.

[外链图片转存失败(img-mIRbG5Xu-1567432898473)(https://hyperledger-fabric.readthedocs.io/en/release-1.4/_images/AppConceptsOverview.png)]

2. Learning about a sample smart contract, FabCar. We use a smart contract written in JavaScript. We’ll inspect the smart contract to learn about the transactions within them, and how they are used by applications to query and update the ledger.

3. Develop a sample application which uses FabCar. Our application will use the FabCar smart contract to query and update car assets on the ledger. We’ll get into the code of the apps and the transactions they create, including querying a car, querying a range of cars, and creating a new car.

After completing this tutorial you should have a basic understanding of how an application is programmed in conjunction with a smart contract to interact with the ledger hosted and replicated on the peers in a Fabric network.

These applications are also compatible with Service Discoveryand Privary data, though we won’t explicitly show how to use our apps to leverage those features.

这一节有三部分

搭建环境
一个JS写的合约FabCar
基于FabCar写一个应用

Set up the blockchain network

This next section requires you to be in the first-network subdirectory within your local clone of the fabric-samples repo.

If you’ve already run through Building Your First Network you will have downloadedfabric-samples and have a network up and running. Before you run this tutorial, you must stop this network:

./byfn.sh down

If you have run through this tutorial before, use the following commands to kill any stale or active containers. Note, this will take down all of your containers whether they’re Fabric related or not.

docker rm -f $(docker ps -aq)
docker rmi -f $(docker images | grep fabcar | awk '{print $3}')

If you don’t have a development environment and the accompanying artifacts for the network and applications, visit the Prerequisites page and ensure you have the necessary dependencies installed on your machine.

Next, if you haven’t done so already, visit theInstall Samples, Binaries and Docker Imagespage and follow the provided instructions. Return to this tutorial once you have cloned the fabric-samples repository, and downloaded the latest stable Fabric images and available utilities.

If you are using Mac OS and running Mojave, you will need to install Xcode.

如果已经按照Building Your First Network进行操作,需要先销毁该环境。注意 docker rm -f ${docker ps -aq} 会销毁包括Fabric在内的所有容器。
如果还没部署环境,先按《
Prerequisites》准备环境,再《
Install Samples, Binaries and Docker Images》,直到完成 git clone fabric-samples 这一步。
注意其中Node.js不能使用Ubuntu自带的repo,要按照node.js官方文档从https://github.com/nodesource/distributions/blob/master/README.md#debinstall 下载repo后安装。
另外,《
Install Samples, Binaries and Docker Images》里的自动安装脚本
curl -sSL http://bit.ly/2ysbOFE | bash -s 因为你知道的原因不能直接下载,可以访问短地址重定向的地址
https://raw.githubusercontent.com/hyperledger/fabric/master/scripts/bootstrap.sh 下载

Launch the network

This next section requires you to be in the fabcar subdirectory within your local clone of the fabric-samples repo.

Launch your network using the startFabric.sh shell script. This command will spin up a blockchain network comprising peers, orderers, certificate authorities and more. It will also install and instantiate a javascript version of the FabCar smart contract which will be used by our application to access the ledger. We’ll learn more about these components as we go through the tutorial.

./startFabric.sh javascript

Alright, you’ve now got a sample network up and running, and the FabCar smart contract installed and instantiated. Let’s install our application pre-requisites so that we can try it out, and see how everything works together.

下边到fabric-samples中的fabcar目录。
运行 ./startFabric.sh javascript, 将创建一个包含节点、排序节点和CA等等的区块链网络,并且安装和实例化一个javascript版本的FabCar合约。
过程中会看到一些错误,大部分是由于创建节点和加入通道的过程还在进行导致的,一般会在第一次重试时资源就绪,不用担心。

Install the application

The following instructions require you to be in thefabcar/javascript subdirectory within your local clone of thefabric-samples repo.

Run the following command to install the Fabric dependencies for the applications. It will take about a minute to complete:

npm install

This process is installing the key application dependencies defined in package.json. The most important of which is the fabric-network class; it enables an application to use identities, wallets, and gateways to connect to channels, submit transactions, and wait for notifications. This tutorial also uses the fabric-ca-client class to enroll users with their respective certificate authorities, generating a valid identity which is then used by fabric-network class methods.

Once npm install completes, everything is in place to run the application. For this tutorial, you’ll primarily be using the application JavaScript files in the fabcar/javascript directory. Let’s take a look at what’s inside:

ls

You should see the following:

enrollAdmin.js  node_modules       package.json  registerUser.js 
invoke.js       package-lock.json  query.js      wallet

There are files for other program languages, for example in the fabcar/typescript directory. You can read these once you’ve used the JavaScript example – the principles are the same.

If you are using Mac OS and running Mojave, you will need to install Xcode.

如下部分要在本地clone的fabric-sample 下的fabcar/javascript 子目录中操作。

首先 npm install 安装 package.json中定义的依赖。其中最重要的是 fabric-network 类供应用使用身份,钱包和连接到通道的网关,提交交易,等待通知。教程还使用 fabric-ca-client 类以相应的认证信息注册用户,生成合法身份以供 fabric-network 类使用。
教程后边主要使用 fabcar/javascript下的脚本。

Enrolling the admin user

The following two sections involve communication with the Certificate Authority. You may find it useful to stream the CA logs when running the upcoming programs by opening a new terminal shell and running docker logs -f ca.example.com.

When we created the network, an admin user — literally called admin — was created as the registrar for the certificate authority (CA). Our first step is to generate the private key, public key, and X.509 certificate for admin using the enroll.js program. This process uses a Certificate Signing Request (CSR) — the private and public key are first generated locally and the public key is then sent to the CA which returns an encoded certificate for use by the application. These three credentials are then stored in the wallet, allowing us to act as an administrator for the CA.

We will subsequently register and enroll a new application user which will be used by our application to interact with the blockchain.

Let’s enroll user admin:

node enrollAdmin.js

This command has stored the CA administrator’s credentials in the wallet directory.

注册管理员
后边两节包括和CA的通讯,通过 docker logs -f ca.example.com 打开日志会很有用。
创建网络的时候,admin作为registar 创建,第一步是用enroll.js 生成admin的私钥公钥和xX.509证书。该过程通过CSR Certificate Signing Request 完成,三个证书都会保存在钱包里。
我们后边注册并登记一个新的应用用户和区块链交互。

Register and enroll user1

Now that we have the administrator’s credentials in a wallet, we can enroll a new user — user1 — which will be used to query and update the ledger:

node registerUser.js

Similar to the admin enrollment, this program uses a CSR to enroll user1 and store its credentials alongside those of admin in the wallet. We now have identities for two separate users — admin and user1 — and these are used by our application.

Time to interact with the ledger…

注册并登记user1
运行 node registerUser.js ,通过CSR注册user1并和admin凭证一起保存到钱包。

Querying the ledger

Each peer in a blockchain network hosts a copy of the ledger, and an application program can query the ledger by invoking a smart contract which queries the most recent value of the ledger and returns it to the application.

区块链网络的每个节点都有一份账本副本,应用通过触发智能合约查询账本的最近数据并返回给应用。
Here is a simplified representation of how a query works:

[外链图片转存失败(img-35z85ewT-1567520663125)(https://hyperledger-fabric.readthedocs.io/en/release-1.4/_images/write_first_app.diagram.1.png)]

Applications read data from the ledger using a query. The most common queries involve the current values of data in the ledger – its world state. The world state is represented as a set of key-value pairs, and applications can query data for a single key or multiple keys. Moreover, the ledger world state can be configured to use a database like CouchDB which supports complex queries when key-values are modeled as JSON data. This can be very helpful when looking for all assets that match certain keywords with particular values; all cars with a particular owner, for example.

应用通过查询从账本读取数据,最常用的查询包含账本里的当前值-全局状态。全局状态以一组键值对表现,账本全局状态可以配置为使用类似CouchDB这样通过把键值对建模为JSON来支持复杂查询的数据库。这有助于按特定值查询,比如在所有车中查询指定车主。

First, let’s run our query.js program to return a listing of all the cars on the ledger. This program uses our second identity – user1 – to access the ledger:

node query.js

通过运行 node query.js来返回账本中所有车的列表。

The output should look like this:

  Wallet path: ...fabric-samples/fabcar/javascript/wallet
  Transaction has been evaluated, result is:
  [{"Key":"CAR0", "Record":{"colour":"blue","make":"Toyota","model":"Prius","owner":"Tomoko"}},
  {"Key":"CAR1", "Record":{"colour":"red","make":"Ford","model":"Mustang","owner":"Brad"}},
  {"Key":"CAR2", "Record":{"colour":"green","make":"Hyundai","model":"Tucson","owner":"Jin Soo"}},
  {"Key":"CAR3", "Record":{"colour":"yellow","make":"Volkswagen","model":"Passat","owner":"Max"}},
  {"Key":"CAR4", "Record":{"colour":"black","make":"Tesla","model":"S","owner":"Adriana"}},
  {"Key":"CAR5", "Record":{"colour":"purple","make":"Peugeot","model":"205","owner":"Michel"}},
  {"Key":"CAR6", "Record":{"colour":"white","make":"Chery","model":"S22L","owner":"Aarav"}},
  {"Key":"CAR7", "Record":{"colour":"violet","make":"Fiat","model":"Punto","owner":"Pari"}},
  {"Key":"CAR8", "Record":{"colour":"indigo","make":"Tata","model":"Nano","owner":"Valeria"}},
  {"Key":"CAR9", "Record":{"colour":"brown","make":"Holden","model":"Barina","owner":"Shotaro"}}]

Let’s take a closer look at this program. Use an editor (e.g. atom or visual studio) and open query.js.

现在看看query.js

The application starts by bringing in scope two key classes from the fabric-network module; FileSystemWallet and Gateway. These classes will be used to locate the user1 identity in the wallet, and use it to connect to the network:

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

通过FileSystemWallet和Gateway定位user1在钱包中的身份,连接到网络。

The application connects to the network using a gateway:

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

This code creates a new gateway and then uses it to connect the application to the network. ccp describes the network that the gateway will access with the identity user1 from wallet. See how the ccp has been loaded from ../../basic-network/connection.json and parsed as a JSON file:

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

创建新的网关用于连接应用到网络。ccp描述了网关通过user1所能连接到的网络,ccp被加载并解析为JSON。

If you’d like to understand more about the structure of a connection profile, and how it defines the network, check out the connection profile topic.
如需深入了解连接配置,查看 the connection profile topic

A network can be divided into multiple channels, and the next important line of code connects the application to a particular channel within the network, mychannel:
一个网络可以被分为多个通道,后边的代码连接应用到网络中的特定通道mychannel

Within this channel, we can access the smart contract fabcar to interact with the ledger:

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

在这个通道里,通过智能合约fabcar和账本交互。

Within fabcar there are many different transactions, and our application initially uses the queryAllCars transaction to access the ledger world state data:

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

fabcar里有多个不同的事务,应用最初使用queryAllCars事务来访问账本的全局状态数据。

The evaluateTransaction method represents one of the simplest interaction with a smart contract in blockchain network. It simply picks a peer defined in the connection profile and sends the request to it, where it is evaluated. The smart contract queries all the cars on the peer’s copy of the ledger and returns the result to the application. This interaction does not result in an update the ledger.
evaluateTransaction 方法展示了智能合约和区块链网络的最简单交互。它简单的选取了一个节点并把请求发给它。智能合约通过查询节点上账本的副本上的所有车返回结果,但并不更新账本。

The FabCar smart contract

Let’s take a look at the transactions within the FabCar smart contract. Navigate to the chaincode/fabcar/javascript/lib subdirectory at the root of fabric-samples and open fabcar.js in your editor.

我们看看FabCar内的事务,打开fabric-sampleschaincode/fabcar/javascript/lib 目录下的 ‘‘fabcar.js’’

See how our smart contract is defined using the Contract class:

class FabCar extends Contract {...

合约在Contract类中定义

Within this class structure, you’ll see that we have the following transactions defined: initLedger, queryCar, queryAllCars, createCar, and changeCarOwner. For example:

async queryCar(ctx, carNumber) {...}
async queryAllCars(ctx) {...}

类的结构中,有多个事务initLedger, queryCar, queryAllCars, createCar, and changeCarOwner

Let’s take a closer look at the queryAllCars transaction to see how it interacts with the ledger.

async queryAllCars(ctx) {

    const startKey = 'CAR0';
    const endKey = 'CAR999';

    const iterator = await ctx.stub.getStateByRange(startKey, endKey);

我们看看queryAllCars 事务。

This code defines the range of cars that queryAllCars will retrieve from the ledger. Every car between CAR0 and CAR999 – 1,000 cars in all, assuming every key has been tagged properly – will be returned by the query. The remainder of the code iterates through the query results and packages them into JSON for the application.
代码定义了queryAllCars从账本中查询车的范围,即 CAR0CAR999 ,所有key被打了正确tag的都将被查询返回。后边的代码遍历结果并返回JSON给应用。

Below is a representation of how an application would call different transactions in a smart contract. Each transaction uses a broad set of APIs such as getStateByRange to interact with the ledger. You can read more about these APIs in detail
下边图示了应用如何调用合约中不同的事务。每个事物使用不同API和账本交互,API信息可以查看 detail

[外链图片转存失败(img-8Az5XeWK-1567520663128)(https://hyperledger-fabric.readthedocs.io/en/release-1.4/_images/RunningtheSample.png)]

We can see our queryAllCars transaction, and another called createCar. We will use this later in the tutorial to update the ledger, and add a new block to the blockchain.
我们可以看到queryAllCarscreateCar事务,后边会讲。

But first, go back to the query program and change the evaluateTransaction request to query CAR4. The query program should now look like this:

const result = await contract.evaluateTransaction('queryCar', 'CAR4');

先回到query 修改evaluateTransaction来查询CAR4

Save the program and navigate back to your fabcar/javascript directory. Now run the query program again:

node query.js

You should see the following:

Wallet path: ...fabric-samples/fabcar/javascript/wallet
Transaction has been evaluated, result is:
   {"colour":"black","make":"Tesla","model":"S","owner":"Adriana"}

保存并运行,可以看到结果变化。

If you go back and look at the result from when the transaction was queryAllCars, you can see that CAR4 was Adriana’s black Tesla model S, which is the result that was returned here.

We can use the queryCar transaction to query against any car, using its key (e.g. CAR0) and get whatever make, model, color, and owner correspond to that car.

Great. At this point you should be comfortable with the basic query transactions in the smart contract and the handful of parameters in the query program.

到此为止,你可以做简单的查询事务了。

Time to update the ledger…

Updating the ledger

Now that we’ve done a few ledger queries and added a bit of code, we’re ready to update the ledger. There are a lot of potential updates we could make, but let’s start by creating a new car.

搞完查询,可以更新账本了,虽然有很多潜在的更新可以做,我们从new 一个car开始。

From an application perspective, updating the ledger is simple. An application submits a transaction to the blockchain network, and when it has been validated and committed, the application receives a notification that the transaction has been successful. Under the covers this involves the process of consensus whereby the different components of the blockchain network work together to ensure that every proposed update to the ledger is valid and performed in an agreed and consistent order.

从应用视角,更新账本很简单。应用提交事务到区块链网络,经过验证和确认,应用收到事务成功的通知。这之下包含了区块链网络的不同模块确保更新提案合法并以认同和一致顺序的方式执行达成共识的过程。

[外链图片转存失败(img-Hf5RKzip-1567606880069)(https://hyperledger-fabric.readthedocs.io/en/release-1.4/_images/write_first_app.diagram.2.png)]

Above, you can see the major components that make this process work. As well as the multiple peers which each host a copy of the ledger, and optionally a copy of the smart contract, the network also contains an ordering service. The ordering service coordinates transactions for a network; it creates blocks containing transactions in a well-defined sequence originating from all the different applications connected to the network.
如上图,可以看到处理工作的主要模块。所有节点都托管一份账本,也可选的托管一份智能合约,网络还包含一个排序服务。排序服务协调网络中的事务,它把连接到网络的不同应用的事务以定义好的顺序创建到区块中。

Our first update to the ledger will create a new car. We have a separate program called invoke.js that we will use to make updates to the ledger. Just as with queries, use an editor to open the program and navigate to the code block where we construct our transaction and submit it to the network:

await contract.submitTransaction('createCar', 'CAR12', 'Honda', 'Accord', 'Black', 'Tom');

我们的第一个更新是create a new car。我们用 invoke.js 来更新账本,程序中如下这一行构建事务并提交到网络。

See how the applications calls the smart contract transaction createCar to create a black Honda Accord with an owner named Tom. We use CAR12 as the identifying key here, just to show that we don’t need to use sequential keys.

程序调用智能合约事务 createCar来创建一个Accord,以CAR12为其身份。

Save it and run the program:

node invoke.js

If the invoke is successful, you will see output like this:

Wallet path: ...fabric-samples/fabcar/javascript/wallet
2018-12-11T14:11:40.935Z - info: [TransactionEventHandler]: _strategySuccess: strategy success for transaction "9076cd4279a71ecf99665aed0ed3590a25bba040fa6b4dd6d010f42bb26ff5d1"
Transaction has been submitted

保存并运行,如果调用成功,可以看到如上输出。

Notice how the invoke application interacted with the blockchain network using the submitTransaction API, rather than evaluateTransaction.

await contract.submitTransaction('createCar', 'CAR12', 'Honda', 'Accord', 'Black', 'Tom');

invoke通过submitTransaction和区块链网络交互,而不是evaluateTransaction。

submitTransaction is much more sophisticated than evaluateTransaction. Rather than interacting with a single peer, the SDK will send the submitTransaction proposal to every required organization’s peer in the blockchain network. Each of these peers will execute the requested smart contract using this proposal, to generate a transaction response which it signs and returns to the SDK. The SDK collects all the signed transaction responses into a single transaction, which it then sends to the orderer. The orderer collects and sequences transactions from every application into a block of transactions. It then distributes these blocks to every peer in the network, where every transaction is validated and committed. Finally, the SDK is notified, allowing it to return control to the application.

submitTransaction比evaluateTransaction复杂,SDK把submitTransaction提案发送到每个需要的组织节点而不是单一节点。每个节点执行使用该提案所需的智能合约,生成事务响应并签名返回SDK。SDK收集签名的事务响应到一个单一事务,发送给排序节点。排序节点从每个应用收集事务并排序到一个交易区块。然后把区块发送到网络中的每个节点,每个事物被验证和提交。最后,SDK被通知,允许返回控制权给应用。

submitTransaction also includes a listener that checks to make sure the transaction has been validated and committed to the ledger. Applications should either utilize a commit listener, or leverage an API like submitTransaction that does this for you.

submitTransaction还包括一个监听器,检查并确保事务被验证和提交到账本。应用或实现一个提交监听器,或者实现一个像submitTransaction的API。

Without doing this, your transaction may not have been successfully orderered, validated, and committed to the ledger.
不这么做,你的事务可能不能成功排序验证并提交到账本。

submitTransaction does all this for the application! The process by which the application, smart contract, peers and ordering service work together to keep the ledger consistent across the network is called consensus, and it is explained in detail in this peers.

submitTransaction为应用作了所有这些。这个包含了应用、合约、节点和排序服务使账本在网络保持一致的的过程即共识。

To see that this transaction has been written to the ledger, go back to query.js and change the argument from CAR4 to CAR12.

回到query.js修改参数从CAR4到CAR12。

In other words, change this:

const result = await contract.evaluateTransaction('queryCar', 'CAR4');

To this:

const result = await contract.evaluateTransaction('queryCar', 'CAR12');

Save once again, then query:

node query.js

Which should return this:

Wallet path: ...fabric-samples/fabcar/javascript/wallet
Transaction has been evaluated, result is:      {"colour":"Black","make":"Honda","model":"Accord","owner":"Tom"}

保存,运行,看到如上的结果。
Congratulations. You’ve created a car and verified that its recorded on the ledger!

So now that we’ve done that, let’s say that Tom is feeling generous and he wants to give his Honda Accord to someone named Dave.

后边,我们把车转送。

To do this, go back to invoke.js and change the smart contract transaction from createCar to changeCarOwner with a corresponding change in input arguments:

await contract.submitTransaction('changeCarOwner', 'CAR12', 'Dave');

在invoke.js中,把createCar改为changeCarOwner。

The first argument — CAR12 — identifies the car that will be changing owners. The second argument — Dave — defines the new owner of the car.

Save and execute the program again:

node invoke.js

Now let’s query the ledger again and ensure that Dave is now associated with the CAR12 key:

node query.js

It should return this result:

Wallet path: ...fabric-samples/fabcar/javascript/wallet
 Transaction has been evaluated, result is:
 {"colour":"Black","make":"Honda","model":"Accord","owner":"Dave"}

The ownership of CAR12 has been changed from Tom to Dave.

CAR12的所有权已经转移了。

In a real world application the smart contract would likely have some access control logic. For example, only certain authorized users may create new cars, and only the car owner may transfer the car to somebody else.

Summary

Now that we’ve done a few queries and a few updates, you should have a pretty good sense of how applications interact with a blockchain network using a smart contract to query or update the ledger. You’ve seen the basics of the roles smart contracts, APIs, and the SDK play in queries and updates and you should have a feel for how different kinds of applications could be used to perform other business tasks and operations.

现在我们做了点查询和更新,你对应用通过智能合约和区块链网络交互去查询和更新账本应该有所了解了。你知道了一些基本规则,以及不同类别的应用如何执行不同商业任务和操作。

Additional resources

As we said in the introduction, we have a whole section on Developing Applications that includes in-depth information on smart contracts, process and data design, a tutorial using a more in-depth Commercial Paper tutorial and a large amount of other material relating to the development of applications.

如我们所说,我们有一整节包含智能合约,过程和数据设计。tutorial 有关于开发应用的大量材料。

你可能感兴趣的:(区块链)