编写你的第一个应用程序(Writing Your First Application)
If you’re not yet familiar with the fundamental architecture of a Fabric network, you may want to visit the Introduction and Building Your First Network documentation prior to continuing.
如果你还不熟悉Fabric网络的基本架构,可以在继续之前访问介绍和构建你的第一个网络文档。
In this section we’ll be looking at a handful of sample programs to see how Fabric apps work. These apps (and the smart contract they use) – collectively known as fabcar
– provide a broad demonstration of Fabric functionality. Notably, we will show the process for interacting with a Certificate Authority and generating enrollment certificates, after which we will leverage these identities to query and update a ledger.
在本节中,我们将介绍一些示例程序,以了解Fabric应用程序的工作原理。 这些应用程序(以及他们使用的智能合约) - 统称为“fabcar” - 提供了Fabric功能的广泛演示。 值得注意的是,我们将展示与证书颁发机构进行交互并生成注册证书的过程,之后我们将利用这些身份来查询和更新帐本。
We’ll go through three principle steps:
我们将通过三个主要步骤:
-
Setting up a development environment. Our application needs a network to interact with, so we’ll download one stripped down to just the components we need for registration/enrollment, queries and updates:
**设置开发环境。**我们的应用程序需要一个网络进行交互,因此我们将下载一个仅限于注册、查询和更新所需的组件:
-
Learning the parameters of the sample smart contract our app will use. Our smart contract contains various functions that allow us to interact with the ledger in different ways. We’ll go in and inspect that smart contract to learn about the functions our applications will be using.
学习我们应用程序将使用的样本智能合约参数。我们的智能合约包含各种功能,允许我们以不同的方式与帐本交互。 我们将进入并检查该智能合约,以了解我们的应用程序将使用的功能。 -
Developing the applications to be able to query and update assets on the ledger. We’ll get into the app code itself (our apps have been written in Javascript) and manually manipulate the variables to run different kinds of queries and updates.
开发应用程序以便能够在帐本上查询和更新资产。我们将进入应用程序代码本身(我们的应用程序已用Javascript编写)并手动操作变量以运行不同类型的查询和更新。
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 (i.e. the peer) on a Fabric network.
完成本教程后,你应该基本了解应用程序如何与智能合约一起编程,来跟Fabric网络上的帐本(即对等节点)进行交互。
设置你的开发环境(Setting up your Dev Environment)
If you’ve already run through Building Your First Network, you should have your dev environment setup and will have downloaded fabric-samples as well as the accompanying artifacts. To run this tutorial, what you need to do now is tear down any existing networks you have, which you can do by issuing the following:
如果你已经完成过构建你的第一个网络,你应该设置你的开发环境,并且已经下载了* fabric -samples *以及附带的工件。 要运行本教程,你现在需要做的就是拆除现有的任何网络,您可以通过发出以下命令来执行此操作:
./byfn.sh down
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 the Install Samples, Binaries and Docker Images page 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.
接下来,如果你还没有这样做,请访问安装示例,二进制文件和Docker镜像页面并按照提供的方式进行操作说明。 一旦克隆了fabric-samples
存储库,并下载了最新的稳定Fabric图像和可用的实用程序,请返回本教程。
At this point everything should be installed. Navigate to the fabcar
subdirectory within your fabric-samples
repository and take a look at what’s inside:
此时应该已安装一切。去到fabric-samples
目录中的fabcar
子目录,看一下里面的内容:
cd fabric-samples/fabcar && ls
You should see the following:
你应该看到以下内容:
enrollAdmin.js invoke.js package.json query.js registerUser.js startFabric.sh
Before starting we also need to do a little housekeeping. Run the following command to kill any stale or active containers:
在开始之前,我们还需要做一些清理工作。 运行以下命令以终止任何陈旧或活动的容器:
docker rm -f $(docker ps -aq)
Clear any cached networks:
清除所有缓存的网络:
# Press 'y' when prompted by the command
docker network prune
And lastly if you’ve already run through this tutorial, you’ll also want to delete the underlying chaincode image for the fabcar
smart contract. If you’re a user going through this content for the first time, then you won’t have this chaincode image on your system:
最后,如果你已经完成过本教程,那么你还需要删除fabcar
智能合约的基础链代码镜像。 如果你是第一次浏览此内容的用户,那么你的系统里将不会显示此链代码图像:
docker rmi dev-peer0.org1.example.com-fabcar-1.0-5c906e402ed29f20260ae42283216aa75549c571e2e380f3615826365d8269ba
安装客户端并启动网络(Install the clients & launch the network)
The following instructions require you to be in the fabcar
subdirectory within your local clone of the fabric-samples
repo. Remain at the root of this subdirectory for the remainder of this tutorial.
以下说明要求你在fabric-samples
repo的本地克隆中的fabcar
子目录中。 在本教程的其余部分中,保留此子目录作为根目录。
Run the following command to install the Fabric dependencies for the applications. We are concerned with fabric-ca-client
which will allow our app(s) to communicate with the CA server and retrieve identity material, and with fabric-client
which allows us to load the identity material and talk to the peers and ordering service.
运行以下命令以安装应用程序的Fabric依赖项。 我们关注fabric-ca-client
,它允许我们的应用程序与CA服务器通信并检索身份资料,以及fabric-client
,它允许我们加载身份资料并与对等节点和排序服务对话。
npm install
(npm可能会下载不了grpc_node.node,用cnpm install
就可以)
Launch your network using the startFabric.sh
shell script. This command will spin up our various Fabric entities and launch a smart contract container for chaincode written in Golang:
使用startFabric.sh
shell脚本启动网络。 此命令将启动我们的各种Fabric实体并为Golang编写的链代码启动智能合约容器:
./startFabric.sh
You also have the option of running this tutorial against chaincode written in Node.js. If you’d like to pursue this route, issue the following command instead:
你还可以选择针对Node.js中编写的链代码运行本教程。 如果你想继续这条路线,请发出以下命令:(其实后面的示例代码都是基于nodejs的)
./startFabric.sh node
(脚本里的版本好像有问题,后面的运行会报错,把-v 1.0
都改成 -v 1.3
就成功了,生成的image名字是dev-peer0.org1.example.com-fabcar-1.3-2669764f414a8258046c9986ea8162cc1a0f489ac939748aa68628d69a438109)
Be aware that the Node.js chaincode scenario will take roughly 90 seconds to complete; perhaps longer. The script is not hanging, rather the increased time is a result of the fabric-shim being installed as the chaincode image is being built.
请注意,Node.js链代码方案大约需要90秒才能完成; 也许更长。 脚本没有挂起,增加的时间是构建链代码镜像时安装fabric-shim的结果。
Alright, now that you’ve got a sample network and some code, let’s take a look at how the different pieces fit together.
好了,现在你已经有了一个示例网络和一些代码,让我们来看看不同的部分是如何组合在一起的。
应用程序如何与网络交互(How Applications Interact with the Network)
For a more in-depth look at the components in our fabcar
network (and how they’re deployed) as well as how applications interact with those components on more of a granular level, see Understanding the Fabcar Network.
要更深入地了解我们的’fabcar`网络中的组件(以及它们如何部署)以及应用程序如何在更细微的级别上与这些组件交互,请参阅“了解Fabcar网络”。
Developers more interested in seeing what applications do – as well as looking at the code itself to see how an application is constructed – should continue. For now, the most important thing to know is that applications use a software development kit (SDK) to access the APIs that permit queries and updates to the ledger.
开发人员更有兴趣了解应用程序做什么 - 以及查看代码本身以查看应用程序的构建方式 - 应该继续。 目前,最重要的是要知道应用程序使用软件开发工具包(SDK)来访问允许查询和更新帐本的** API**。
了解Fabcar网络(Understanding the Fabcar Network)
Fabcar was designed to leverage a network stripped down to only the components necessary to run an application. And even with that level of simplification, the ./startFabric.sh
script takes care of the installation and configuration not baked into the network itself.
Fabcar旨在利用仅剥离运行应用程序所需组件的网络。即使具有这种简化程度,./startFabric.sh
脚本也会处理未融入网络本身的安装和配置。
Obscuring the underpinnings of the network to that degree is fine for the majority of application developers. They don’t necessarily need to know how network components actually work in detail in order to create their app.
对于大多数应用程序开发人员而言,将网络的基础模糊到这种程度是很好的。他们为了创建应用程序,不一定需要知道网络组件如何实际工作。
But for those who do want to know about the fun stuff going on under the covers, let’s go through how applications connect to the network and how they propose queries and updates on a more granular level, as well as point out the differences between a small scale test network like Fabcar and how apps will usually end up working in the real world.
但对于那些想要了解隐藏在下面的有趣内容的人来说,让我们了解一下应用程序如何连接到网络,以及他们如何在更细粒度的层面上提出查询和更新,以及指出像Fabcar这样的小规模测试网络和应用程序如何在现实世界中运作的差异。
We’ll also point you to where you can get detailed information about how Fabric networks are created and how a transaction flow works beyond the scope of the role an application plays.
我们还将指出你可以获得有关如何创建Fabric网络以及交易流程如何超出应用程序角色范围详细信息的位置。
Fabcar网络组件(Components of the Fabcar Network)
Fabcar uses the “basic-network” sample as its limited development network. It consists of a single peer node configured to use CouchDB as the state database, a single “solo” ordering node, a certificate authority (CA) and a CLI container for executing commands.
Fabcar使用“basic-network”样本作为其有限的开发网络。 它由一个配置为使用CouchDB作为状态数据库的对等节点,一个“单”排序节点,一个证书颁发机构(CA)和一个用于执行命令的CLI容器组成。
For detailed information on these components and what they do, refer to Building Your First Network.
有关这些组件及其功能的详细信息,请参阅构建你的第一个网络。
These components are bootstrapped by the ./startFabric.sh
script, which also:
这些组件由./startFabric.sh脚本引导,该脚本还包括:
- creates a channel and joins the peer to the channel
创建一个通道并将对等节点连接到通道 - installs the
fabcar
smart contract onto the peer’s file system and instantiates it on the channel (instantiate starts a container)
将fabcar
智能合约安装到对等节点的文件系统上并在通道上实例化(实例化会启动一个容器) - calls the
initLedger
function to populate the channel ledger with 10 unique cars
调用initLedger
函数用10个不同的汽车填充通道帐本
These operations would typically be done by an organizational or peer admin. The script uses the CLI to execute these commands, however there is support in the SDK as well. Refer to the Hyperledger Fabric Node SDK repo for example scripts.
这些操作通常由组织或节点管理员完成。 该脚本使用CLI执行这些命令,但SDK中也支持。 有关示例脚本,请参阅Hyperledger Fabric Node SDK存储库。
应用程序如何与网络交互(How an Application Interacts with the Network)
Applications use APIs to invoke smart contracts. These smart contracts are hosted in the network and identified by name and version. For example, our chaincode container is titled - dev-peer0.org1.example.com-fabcar-1.0
- where the name is fabcar
, the version is 1.0
, and the peer it is running against is dev-peer0.org1.example.com
.
应用程序使用** API **来调用智能合约。这些智能合约托管在网络中,并通过名称和版本进行标识。例如,我们的链代码容器标题为 - dev-peer0.org1.example.com-fabcar-1.0
- 其中名称为fabcar
,版本为1.0
,它运行的对等节点名称为dev-peer0.org1.example.com
。
APIs are accessible with an SDK. For purposes of this exercise, we’re using the Hyperledger Fabric Node SDK though there is also a Java SDK and CLI that can be used to drive transactions. SDKs encapsulate all access to the ledger by allowing an application to communicate with smart contracts, run queries, or receive ledger updates. These APIs use several different network addresses and are run with a set of input parameters.
可以使用SDK访问API。出于本练习的目的,我们使用Hyperledger Fabric Node SDK,尽管还有一个可用于驱动交易的Java SDK和CLI。 SDK通过允许应用程序与智能合约进行通信,运行查询或接收帐本更新来封装对帐本的所有访问。这些API使用多个不同的网络地址,并使用一组输入参数运行。
Smart contracts are installed by a peer administrator and then instantiated on a channel by an identity fulfilling the chaincode’s instantiation policy, which by default is comprised of channel administrators. The instantiation of the smart contract follows the same transaction flow as a normal invocation - endorse, order, validate, commit - and is a prerequisite to interacting with a chaincode container. The script that launched our simplified Fabcar test network took care of the installation and instantiation for us.
智能合约由节点管理员安装,然后通过履行链代码实例化策略的标识在通道上实例化,该策略默认由通道管理员组成。智能合约的实例化遵循与正常调用相同的交易流程 - 背书、排序、验证、提交 - 并且是与链代码容器交互的先决条件。启动我们简化的Fabcar测试网络的脚本负责安装和实例化。
查询(Query)
Queries are the simplest kind of invocation: a call and response. The most common query will interrogate the state database for the current value associated with a key (GetState
). However, the chaincode shim interface also allows for different types of Get
calls (e.g. GetHistoryForKey
or GetCreator
).
查询是最简单的调用:调用和响应。最常见的查询将询问状态数据库以获取与键关联的当前值(GetState
)。但是,chaincode shim interface也允许不同类型的Get
调用(例如GetHistoryForKey
或GetCreator
)。
In our example, the peer holds a hash chain of all transactions and maintains chaincode state through use of a state database, which in our case is a CouchDB container. CouchDB provides the added functionality of rich queries, contingent upon the chaincode data (key/val pairs) being modeled as JSON. When we call the GetState
API in our smart contract, we are retrieving the JSON value associated with a car from the CouchDB state database.
在我们的示例中,对等节点拥有所有交易的哈希链,并通过使用状态数据库来维护链代码状态,在我们的示例中,状态数据库是CouchDB容器。 CouchDB提供了丰富查询的附加功能,这取决于被建模为JSON的链代码数据(键/值对)。当我们在智能合约中调用GetState
API时,我们将从CouchDB状态数据库中检索与汽车关联的JSON值。
Queries are constructed by identifying a peer, a chaincode, a channel and a set of inputs (e.g. the key) for an available chaincode function and then utilizing the chain.queryByChaincode
API to send the query to the peer. The corresponding value to the supplied inputs is returned to the application client as a response.
通过识别的对等节点,链代码,通道和一组可用链代码函数的输入(例如,密钥),然后利用chain.queryByChaincode
API将查询发送给对等节点来构建查询。提供的输入的相应值将作为响应返回给应用程序客户端。
更新(Updates)
Ledger updates start with an application generating a transaction proposal. As with query, a request is constructed to identify a peer, chaincode, channel, function, and set of inputs for the transaction. The program then calls the channel.SendTransactionProposal
API to send the transaction proposal to the peer(s) for endorsement.
帐本更新从生成交易提案的应用程序开始。与查询一样,构造请求以识别交易的对等节点,链代码,通道,函数和输入集组成。然后程序调用channel.SendTransactionProposal
API将交易提案发送给对等节点以进行背书。
The network (i.e. the endorsing peer(s)) returns a proposal response, which the application uses to build and sign a transaction request. This request is sent to the ordering service by calling the channel.sendTransaction
API. The ordering service bundles the transaction into a block and delivers it to all peers on a channel for validation (the Fabcar network has only one peer and one channel).
网络(即背书节点)返回提案响应,应用程序使用该提案响应来构建和签署交易请求。通过调用channel.sendTransaction
API将此请求发送到排序服务。排序服务将交易捆绑成区块并将其传递到通道上的所有对等节点以进行验证(Fabcar网络仅具有一个对等节点和一个通道)。
Finally the application uses the Peer channel-based event services to register for events associated with a specific transaction ID so that the application can be notified about the fate of a transaction (i.e. valid or invalid).
最后,应用程序使用节点基于通道的事件服务注册与特定交易ID关联的事件,以便应用程序可以通知交易的命运(即有效或无效)。
更多信息(For More Information)
To learn more about how a transaction flow works beyond the scope of an application, check out Transaction Flow.
要了解有关交易流程超出应用程序范围的更多信息,请查看交易流程。
To get started developing chaincode, read Chaincode for Developers.
要开始开发链代码,请阅读Chaincode for Developers。
For more information on how endorsement policies work, check out Endorsement policies.
有关认可策略如何运作的更多信息,请查看背书策略。
For a deeper dive into the architecture of Hyperledger Fabric, check out Architecture Explained.
要深入了解Hyperledger Fabric的架构,请查看结构说明。
注册管理员用户(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.
以下两节涉及与证书颁发机构的沟通。 你可能会发现在运行即将推出的程序时输出CA日志非常有用。
To stream your CA logs, split your terminal or open a new shell and issue the following:
要输出CA日志,请拆分终端或打开新shell并发出以下命令:
docker logs -f ca.example.com
Now hop back to your terminal with the fabcar
content…
现在跳回fabcar
内容的终端…
When we launched our network, an admin user – admin
– was registered with our Certificate Authority. Now we need to send an enroll call to the CA server and retrieve the enrollment certificate (eCert) for this user. We won’t delve into enrollment details here, but suffice it to say that the SDK and by extension our applications need this cert in order to form a user object for the admin. We will then use this admin object to subsequently register and enroll a new user. Send the admin enroll call to the CA server:
当我们启动网络时,管理员用户 - “admin” - 已在我们的证书颁发机构注册。 现在,我们需要向CA服务器发送注册呼叫,并为该用户检索注册证书(eCert)。 我们不会在这里深入研究注册详细信息,但只需说明SDK和扩展我们的应用程序需要此证书才能形成管理员的用户对象。 然后,我们将使用此管理对象随后登记并注册新用户。 将管理员注册呼叫发送到CA服务器:
node enrollAdmin.js
This program will invoke a certificate signing request (CSR) and ultimately output an eCert and key material into a newly created folder – hfc-key-store
– at the root of this project. Our apps will then look to this location when they need to create or load the identity objects for our various users.
该程序将调用证书签名请求(CSR),并最终将eCert和密钥材料输出到新创建的文件夹 - hfc-key-store
- 在项目的根目录下。 然后,当我们的应用需要为各种用户创建或加载身份对象时,他们会查看此位置。
登记和注册user1
(Register and Enroll user1
)
With our newly generated admin eCert, we will now communicate with the CA server once more to register and enroll a new user. This user – user1
– will be the identity we use when querying and updating the ledger. It’s important to note here that it is the admin
identity that is issuing the registration and enrollment calls for our new user (i.e. this user is acting in the role of a registrar). Send the register and enroll calls for user1
:
使用我们新生成的管理员eCert,我们现在将再次与CA服务器通信以登记和注册新用户。 此用户 - user1
- 将是我们在查询和更新帐本时使用的标识。 这里需要注意的是,admin
身份是为我们的新用户发出注册和注册呼叫(即该用户扮演注册者的角色)。 发送登记并注册user1
:
node registerUser.js
Similar to the admin enrollment, this program invokes a CSR and outputs the keys and eCert into the hfc-key-store
subdirectory. So now we have identity material for two separate users – admin
& user1
. Time to interact with the ledger…
与admin注册类似,该程序调用CSR并将键和eCert输出到hfc-key-store
子目录中。 所以现在我们有两个独立用户的身份材料 - admin
和user1
。 是时候与账本互动了…
查询账本(Querying the Ledger)
Queries are how you read data from the ledger. This data is stored as a series of key-value pairs, and you can query for the value of a single key, multiple keys, or – if the ledger is written in a rich data storage format like JSON – perform complex searches against it (looking for all assets that contain certain keywords, for example).
查询是你从帐本中读取数据的方式。 此数据存储为一系列键值对,你可以查询单个键、多个键的值,或者 - 如果帐本是以JSON等丰富的数据存储格式编写的 - 对其执行复杂的搜索( 例如寻找包含某些关键字的所有资产)。
This is a representation of how a query works:
这是表示查询如何工作的:
First, let’s run our query.js
program to return a listing of all the cars on the ledger. We will use our second identity – user1
– as the signing entity for this application. The following line in our program specifies user1
as the signer:
首先,让我们运行query.js
程序返回帐本中所有汽车的清单。 我们将使用我们的第二个身份 - user1
- 作为此应用程序的签名实体。 我们程序中的以下行指定user1
作为签名者:
fabric_client.getUserContext('user1', true);
Recall that the user1
enrollment material has already been placed into our hfc-key-store
subdirectory, so we simply need to tell our application to grab that identity. With the user object defined, we can now proceed with reading from the ledger. A function that will query all the cars, queryAllCars
, is pre-loaded in the app, so we can simply run the program as is:
回想一下user1
注册材料已经放入我们的hfc-key-store
子目录中,所以我们只需要告诉我们的应用程序获取该标识。 通过定义用户对象,我们现在可以继续从账本中读取。 将查询所有汽车的queryAllCars
函数预先加载到应用程序中,因此我们可以按原样运行程序:
node query.js
It should return something like this:
它应该返回这样的东西:
Successfully loaded user1 from persistence
Query has completed, checking results
Response 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"}}]
These are the 10 cars. A black Tesla Model S owned by Adriana, a red Ford Mustang owned by Brad, a violet Fiat Punto owned by Pari, and so on. The ledger is key-value based and, in our implementation, the key is CAR0
through CAR9
. This will become particularly important in a moment.
这些是10辆车。 由Adriana拥有的黑色特斯拉Model S,由Brad拥有的红色福特Mustang,Pari拥有的紫色菲亚特Punto等等。 账本是基于键值的,在我们的实现中,键值是CAR0
到CAR9
。 这在一会将变得特别重要。
Let’s take a closer look at this program. Use an editor (e.g. atom or visual studio) and open query.js
.
让我们仔细看看这个程序。 使用编辑器(例如atom或vscode)打开query.js
。
The initial section of the application defines certain variables such as channel name, cert store location and network endpoints. In our sample app, these variables have been baked-in, but in a real app these variables would have to be specified by the app dev.
应用程序的初始部分定义了某些变量,例如通道名称,证书存储位置和网络端点。 在我们的示例应用程序中,这些变量已经被内置,但在实际应用程序中,这些变量必须由开发人员指定。
var channel = fabric_client.newChannel('mychannel');
var peer = fabric_client.newPeer('grpc://localhost:7051');
channel.addPeer(peer);
var member_user = null;
var store_path = path.join(__dirname, 'hfc-key-store');
console.log('Store path:'+store_path);
var tx_id = null;
This is the chunk where we construct our query:
这是我们构造查询的块:
// queryCar chaincode function - requires 1 argument, ex: args: ['CAR4'],
// queryAllCars chaincode function - requires no arguments , ex: args: [''],
const request = {
//targets : --- letting this default to the peers assigned to the channel
chaincodeId: 'fabcar',
fcn: 'queryAllCars',
args: ['']
};
When the application ran, it invoked the fabcar
chaincode on the peer, ran the queryAllCars
function within it, and passed no arguments to it.
当应用程序运行时,它调用对等节点上的fabcar
链代码,在其中运行queryAllCars
函数,并且不传递任何参数。
To take a look at the available functions within our smart contract, navigate to the chaincode/fabcar/go
subdirectory at the root of fabric-samples
and open fabcar.go
in your editor.
要查看我们智能合约中的可用功能,请导航到fabric-samples
根目录下的chaincode/fabcar/go
子目录,并在编辑器中打开fabcar.go
。
These same functions are defined within the Node.js version of the fabcar
chaincode.
这些相同的函数在fabcar
链代码的Node.js版本中定义。(chaincode/fabcar/node/fabcar.js)
You’ll see that we have the following functions available to call: initLedger
, queryCar
, queryAllCars
, createCar
, and changeCarOwner
.
你将看到我们可以调用以下函数:initLedger
,queryCar
,queryAllCars
,createCar
和changeCarOwner
。
Let’s take a closer look at the queryAllCars
function to see how it interacts with the ledger.
让我们仔细看看queryAllCars
函数,看看它如何与账本交互。
func (s *SmartContract) queryAllCars(APIstub shim.ChaincodeStubInterface) sc.Response {
startKey := "CAR0"
endKey := "CAR999"
resultsIterator, err := APIstub.GetStateByRange(startKey, endKey)
This defines the range of queryAllCars
. Every car between CAR0
and CAR999
– 1,000 cars in all, assuming every key has been tagged properly – will be returned by the query.
这定义了queryAllCars
的范围。 CAR0
和CAR999
之间的每辆车 - 总共1,000辆车,假设每个键都已正确标记 - 将由查询返回。
Below is a representation of how an app would call different functions in chaincode. Each function must be coded against an available API in the chaincode shim interface, which in turn allows the smart contract container to properly interface with the peer ledger.
下面是一个应用程序如何在链代码中调用不同函数的表示。 必须根据链代码垫片接口中的可用API对每个函数进行编码,这反过来允许智能合约容器与对等节点帐本正确交互。
We can see our queryAllCars
function, as well as one called createCar
, that will allow us to update the ledger and ultimately append a new block to the chain in a moment.
我们可以看到queryAllCars
函数,以及一个名为createCar
的函数,它将允许我们更新帐本并最终在一会将新区块附加到链中。
But first, go back to the query.js
program and edit the constructor request to query CAR4
. We do this by changing the function in query.js
from queryAllCars
to queryCar
and passing CAR4
as the specific key.
但首先,回到query.js
程序并编辑构造请求以查询CAR4
。 我们通过将query.js
中的函数从queryAllCars
更改为queryCar
并将CAR4
作为特定键传递来实现。
The query.js
program should now look like this:
query.js
程序现在应该如下所示:
const request = {
//targets : --- letting this default to the peers assigned to the channel
chaincodeId: 'fabcar',
fcn: 'queryCar',
args: ['CAR4']
};
Save the program and navigate back to your fabcar
directory. Now run the program again:
保存程序并导航回fabcar
目录。 现在再次运行程序:
node query.js
You should see the following:
你将看到如下内容:
{"colour":"black","make":"Tesla","model":"S","owner":"Adriana"}
If you go back and look at the result from when we queried every car before, you can see that CAR4
was Adriana’s black Tesla model S, which is the result that was returned here.
如果你回过头来查看我们之前查询过每辆车的结果,你可以看到CAR4
是Adriana的黑色特斯拉型号S,这是在这里返回的结果。
Using the queryCar
function, we can query against any key (e.g. CAR0
) and get whatever make, model, color, and owner correspond to that car.
使用queryCar
函数,我们可以查询任何键(例如CAR0
)并获得与该汽车相对应的任何品牌,型号,颜色和所有者。
Great. At this point you should be comfortable with the basic query functions 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 car.
现在我们已经完成了一些帐本查询并添加了一些代码,我们已准备好更新帐本。 我们可以做很多可能的更新,但让我们从创建汽车开始吧。
Below we can see how this process works. An update is proposed, endorsed, then returned to the application, which in turn sends it to be ordered and written to every peer’s ledger:
下面我们可以看到这个过程如何运作。 提议更新、背书,然后返回给应用程序,然后应用程序将其发送给排序节点并写入每个对等节点的帐本:
Our first update to the ledger will be to create a new car. We have a separate Javascript program – invoke.js
– that we will use to make updates. Just as with queries, use an editor to open the program and navigate to the code block where we construct our invocation:
我们对账本的第一次更新将是创建一辆新车。 我们有一个单独的Javascript程序 - invoke.js
- 我们将用它来进行更新。 与查询一样,使用编辑器打开程序并导航到构建调用的代码块:
// createCar chaincode function - requires 5 args, ex: args: ['CAR12', 'Honda', 'Accord', 'Black', 'Tom'],
// changeCarOwner chaincode function - requires 2 args , ex: args: ['CAR10', 'Barry'],
// must send the proposal to endorsing peers
var request = {
//targets: let default to the peer assigned to the client
chaincodeId: 'fabcar',
fcn: '',
args: [''],
chainId: 'mychannel',
txId: tx_id
};
You’ll see that we can call one of two functions – createCar
or changeCarOwner
. First, let’s create a red Chevy Volt and give it to an owner named Nick. We’re up to CAR9
on our ledger, so we’ll use CAR10
as the identifying key here. Edit this code block to look like this:
你将看到我们可以调用两个函数之一 - createCar
或changeCarOwner
。 首先,让我们创建一个红色雪佛兰Volt并将其交给名为Nick的车主。 我们的账本上有CAR9
,所以我们在这里使用CAR10
作为识别键。 编辑此代码块如下所示:
var request = {
//targets: let default to the peer assigned to the client
chaincodeId: 'fabcar',
fcn: 'createCar',
args: ['CAR10', 'Chevy', 'Volt', 'Red', 'Nick'],
chainId: 'mychannel',
txId: tx_id
};
Save it and run the program:
保存并允许程序:
node invoke.js
There will be some output in the terminal about ProposalResponse
and promises. However, all we’re concerned with is this message:
终端中会有一些关于ProposalResponse
和promises的输出。 但是,我们所关注的是这条消息:
The transaction has been committed on peer localhost:7053
To see that this transaction has been written, go back to query.js
and change the argument from CAR4
to CAR10
.
要查看此交易已写入,请返回query.js
并将参数从CAR4
更改为CAR10
。
In other words, change this:
换句话说,把这些:
const request = {
//targets : --- letting this default to the peers assigned to the channel
chaincodeId: 'fabcar',
fcn: 'queryCar',
args: ['CAR4']
};
To this:
变成这些:
const request = {
//targets : --- letting this default to the peers assigned to the channel
chaincodeId: 'fabcar',
fcn: 'queryCar',
args: ['CAR10']
};
Save once again, then query:
再次保存,然后查询:
node query.js
Which should return this:
应该返回:
Response is {"colour":"Red","make":"Chevy","model":"Volt","owner":"Nick"}
Congratulations. You’ve created a car!
恭喜你新增了一辆车!
So now that we’ve done that, let’s say that Nick is feeling generous and he wants to give his Chevy Volt to someone named Dave.
既然现在我们已经有了车,让我们说Nick感到很慷慨,他想把他的雪佛兰伏特送给名叫Dave的人。
To do this go back to invoke.js
and change the function from createCar
to changeCarOwner
and input the arguments like this:
要做到这一点,请返回invoke.js
并将函数从createCar
更改为changeCarOwner
并输入如下参数:
var request = {
//targets: let default to the peer assigned to the client
chaincodeId: 'fabcar',
fcn: 'changeCarOwner',
args: ['CAR10', 'Dave'],
chainId: 'mychannel',
txId: tx_id
};
The first argument – CAR10
– reflects the car that will be changing owners. The second argument – Dave
– defines the new owner of the car.
第一个参数 - CAR10
- 反映了将改变车主的汽车。第二个参数 - Dave
- 定义了汽车的新所有者。
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 CAR10 key:
现在让我们再次查询帐本并确保Dave现在与CAR10键相关联:
node query.js
It should return this result:
应该返回这个结果:
Response is {"colour":"Red","make":"Chevy","model":"Volt","owner":"Dave"}
The ownership of CAR10
has been changed from Nick to Dave.
CAR10
的所有权已从Nick改为Dave。
In a real world application the chaincode 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 the network. 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.
现在我们已经完成了一些查询和一些更新,你应该非常清楚应用程序如何与网络交互。 你已经看到了智能合约、API和SDK在查询和更新中扮演的角色的基础知识,你应该了解如何使用不同类型的应用程序来执行其他业务任务和操作。
In subsequent documents we’ll learn how to actually write a smart contract and how some of these more low level application functions can be leveraged (especially relating to identity and membership services).
在随后的文档中,我们将学习如何实际写一个智能合约,以及如何利用其中一些更低级别的应用程序功能(特别是与身份和会员服务相关)。
其他资源(Additional Resources)
The Hyperledger Fabric Node SDK repo is an excellent resource for deeper documentation and sample code. You can also consult the Fabric community and component experts on Hyperledger Rocket Chat.
Hyperledger Fabric Node SDK资料库是更深入文档和示例代码的绝佳资源。 你还可以在Hyperledger Rocket Chat上咨询Fabric社区和组件专家。