Skip to content

Commit a42ddf6

Browse files
committed
[FAB-4430] - Sample App
Doc and images for writing your first app Add to TOC [ci-skip] Change-Id: I55e3299f70f89785d239fd6bcf74f010097586cd Signed-off-by: Nick Gaski <[email protected]>
1 parent 6b63511 commit a42ddf6

5 files changed

+366
-0
lines changed
80 KB
Loading
82.1 KB
Loading
83 KB
Loading

docs/source/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ Before diving in, watch how Fabric is `Building a Blockchain for Business
4646
:caption: Tutorials
4747

4848
build_network
49+
write_first_app
4950
chaincode
5051
videos
5152

docs/source/write_first_app.rst

+365
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,365 @@
1+
Writing Your First Application
2+
==============================
3+
4+
The goal of this document is to show the tasks and provide a baseline for writing
5+
your first application against a Hyperledger Fabric network (a.k.a. Fabric).
6+
7+
At the most basic level, applications on a blockchain network are what enable
8+
users to **query** a ledger (asking for specific records it contains), or to
9+
**update** it (adding records to it).
10+
11+
Our application, composed in Javascript, leverages the node.js SDK to interact
12+
with the network (where our ledger exists). This tutorial will guide you through
13+
the three steps involved in writing your first application.
14+
15+
**1. Starting a test fabric blockchain network.** We need some basic components
16+
in our Fabric in order to query and update the ledger. These components --
17+
a peer node, ordering node and Certificate Authority -- serve as the backbone of
18+
our network; we'll also have a CLI container used for a few administrative commands.
19+
A single script will download and launch this test network.
20+
21+
**2. Learning the parameters of the sample smart contract our app will use.** Our
22+
smart contracts contain various functions that allow us to interact with the ledger
23+
in different ways. For example, we can read data holistically or on a more granular
24+
level.
25+
26+
**3. Developing the application to be able to query and update fabric records.**
27+
We provide two sample applications -- one for querying the ledger and another for
28+
updating it. Our app will use the SDK APIs to interact with the network and
29+
ultimately call these functions.
30+
31+
After completing this tutorial, you should have a basic understanding of how
32+
an application, using the Fabric SDK for Node.js, is programmed in conjunction with a smart contract
33+
to interact with the ledger on a Fabric network.
34+
35+
First, let's launch our test network...
36+
37+
Getting a Test Network
38+
----------------------
39+
40+
Visit the :doc:`prereqs` page and ensure you have the necessary dependencies installed
41+
on your machine.
42+
43+
Now determine a working directory where you want to clone the fabric-samples repo. Issue
44+
the clone command and change into the ``fabcar`` subdirectory
45+
46+
.. code:: bash
47+
48+
git clone https://github.com/hyperledger/fabric-samples.git
49+
cd fabric-samples/fabcar
50+
51+
This subdirectory -- ``fabcar`` -- contains the scripts
52+
and application code to run the sample app. Issue an ``ls`` from
53+
this directory. You should see the following:
54+
55+
.. code:: bash
56+
57+
chaincode invoke.js network package.json query.js startFabric.sh
58+
59+
Now use the ``startFabric.sh`` script to launch the network.
60+
61+
.. note:: The following command downloads and extracts the Fabric docker images, so it
62+
will take a few minutes to complete.
63+
64+
.. code:: bash
65+
66+
./startFabric.sh
67+
68+
For the sake of brevity, we won't delve into the details of what's happening with
69+
this command. Here's a quick synopsis:
70+
71+
* launches a peer node, ordering node, Certificate Authority and CLI container
72+
* creates a channel and joins the peer to the channel
73+
* installs smart contract (i.e. chaincode) onto the peer's file system and instantiates said chaincode on the channel; instantiate starts a chaincdoe container
74+
* calls the ``initLedger`` function to populate the channel ledger with 10 unique cars
75+
76+
.. note:: These operations will typically be done by an organizational or peer admin. The script uses the
77+
CLI to execute these commands, however there is support in the SDK as well.
78+
Refer to the `Hyperledger Fabric Node SDK repo <https://github.com/hyperledger/fabric-sdk-node>`__
79+
for example scripts.
80+
81+
Issue a ``docker ps`` command to reveal the processes started by the ``startFabric.sh`` script.
82+
You can learn more about the details and mechanics of these operations in the
83+
:doc:`build_network` section. Here we'll just focus on the application. The following picture
84+
provides a simplistic representation of how the application interacts with the Fabric network.
85+
86+
.. image:: images/AppConceptsOverview.png
87+
88+
Alright, now that you’ve got a sample network and some code, let’s take a
89+
look at how the different pieces fit together.
90+
91+
How Applications Interact with the Network
92+
------------------------------------------
93+
94+
Applications use **APIs** to invoke smart contracts (referred to in Fabric as "chaincode").
95+
These smart contracts are hosted in the network and identified by name and version.
96+
For example, our chaincode container is titled - ``dev-peer0.org1.example.com-fabcar-1.0`` - where
97+
the name is ``fabcar``, the version is ``1.0`` and the peer it is running against is ``dev-peer0.org1.example.com``.
98+
99+
APIs are accessible with a software development kit (SDK). For purposes of this
100+
exercise, we'll be using the `Hyperledger Fabric Node SDK
101+
<https://fabric-sdk-node.github.io/>`__ though there is also a Java SDK and
102+
CLI that can be used to develop applications.
103+
104+
Querying the Ledger
105+
-------------------
106+
Queries are how you read data from the ledger. You can query for the value
107+
of a single key, multiple keys, or -- if the ledger is written in a rich data storage
108+
format like JSON -- perform complex searches against it (looking for all
109+
assets that contain certain keywords, for example).
110+
111+
.. image:: images/QueryingtheLedger.png
112+
113+
As we said earlier, our sample network has an active chaincode container and
114+
a ledger that has been primed with 10 different cars. We also have some
115+
sample Javascript code - ``query.js`` - in the ``fabcar`` directory that
116+
can be used to query the ledger for details on the cars.
117+
118+
Before we take a look at how that app works, we need to install the SDK node
119+
modules in order for our program to function. From your ``fabcar`` directory,
120+
issue the following:
121+
122+
.. code:: bash
123+
124+
npm install
125+
126+
.. note:: You will issue all subsequent commands from the ``fabcar`` directory.
127+
128+
Now we can run our javascript programs. First, let's run our ``query.js``
129+
program to return a listing of all the cars on the ledger. A function that
130+
will query all the cars, ``queryAllCars``, is pre-loaded in the app,
131+
so we can simply run the program as is:
132+
133+
.. code:: bash
134+
135+
node query.js
136+
137+
It should return something like this:
138+
139+
.. code:: json
140+
141+
Query result count = 1
142+
Response is [{"Key":"CAR0", "Record":{"colour":"blue","make":"Toyota","model":"Prius","owner":"Tomoko"}},
143+
{"Key":"CAR1", "Record":{"colour":"red","make":"Ford","model":"Mustang","owner":"Brad"}},
144+
{"Key":"CAR2", "Record":{"colour":"green","make":"Hyundai","model":"Tucson","owner":"Jin Soo"}},
145+
{"Key":"CAR3", "Record":{"colour":"yellow","make":"Volkswagen","model":"Passat","owner":"Max"}},
146+
{"Key":"CAR4", "Record":{"colour":"black","make":"Tesla","model":"S","owner":"Adriana"}},
147+
{"Key":"CAR5", "Record":{"colour":"purple","make":"Peugeot","model":"205","owner":"Michel"}},
148+
{"Key":"CAR6", "Record":{"colour":"white","make":"Chery","model":"S22L","owner":"Aarav"}},
149+
{"Key":"CAR7", "Record":{"colour":"violet","make":"Fiat","model":"Punto","owner":"Pari"}},
150+
{"Key":"CAR8", "Record":{"colour":"indigo","make":"Tata","model":"Nano","owner":"Valeria"}},
151+
{"Key":"CAR9", "Record":{"colour":"brown","make":"Holden","model":"Barina","owner":"Shotaro"}}]
152+
153+
These are the 10 cars. A black Tesla Model S owned by Adriana, a red Ford Mustang
154+
owned by Brad, a violet Fiat Punto owned by someone named Pari, and so on. The ledger
155+
is key/value based and in our implementation the key is ``CAR0`` through ``CAR9``.
156+
This will become particularly important in a moment.
157+
158+
Now let's see what it looks like under the hood (if you'll forgive the pun).
159+
Use an editor (e.g. atom or visual studio) and open the ``query.js`` program.
160+
161+
The inital section of the application defines certain variables such as chaincode ID, channel name
162+
and network endpoints:
163+
164+
.. code:: bash
165+
166+
var options = {
167+
wallet_path : path.join(__dirname, './network/creds'),
168+
user_id: 'PeerAdmin',
169+
channel_id: 'mychannel',
170+
chaincode_id: 'fabcar',
171+
network_url: 'grpc://localhost:7051',
172+
173+
This is the chunk where we construct our query:
174+
175+
.. code:: bash
176+
177+
// queryCar - requires 1 argument, ex: args: ['CAR4'],
178+
// queryAllCars - requires no arguments , ex: args: [''],
179+
const request = {
180+
chaincodeId: options.chaincode_id,
181+
txId: transaction_id,
182+
fcn: 'queryAllCars',
183+
args: ['']
184+
185+
We define the ``chaincode_id`` variable as ``fabcar`` -- allowing us to target this specific chaincode -- and
186+
then call the ``queryAllCars`` function defined within that chaincode.
187+
188+
When we issued the ``node query.js`` command earlier, this specific function was
189+
called to query the ledger. However, this isn't the only function that we can pass.
190+
191+
To take a look at the others, navigate to the ``chaincode`` subdirectory and open
192+
``fabcar.go`` in your editor. You'll see that we have the following functions available
193+
to call - ``initLedger``, ``queryCar``, ``queryAllCars``, ``createCar`` and ``changeCarOwner``.
194+
Let's take a closer look at the ``queryAllCars`` function to see how it interacts with the
195+
ledger.
196+
197+
.. code:: bash
198+
199+
func (s *SmartContract) queryAllCars(APIstub shim.ChaincodeStubInterface) sc.Response {
200+
201+
startKey := "CAR0"
202+
endKey := "CAR999"
203+
204+
resultsIterator, err := APIstub.GetStateByRange(startKey, endKey)
205+
206+
The function uses the Fabric's shim interface ``GetStateByRange`` to return
207+
ledger data between the args of ``startKey`` and ``endKey``. Those keys are
208+
defined as ``CAR0`` and ``CAR999`` respectively. Therefore, we could theoretically
209+
create 1,000 cars (assuming the keys are tagged properly) and a ``queryAllCars`` would
210+
reveal every one.
211+
212+
Below is a representation of how an app would call different functions in chaincode.
213+
214+
.. image:: images/RunningtheSample.png
215+
216+
We can see our ``queryAllCars`` function up there, as well as one called ``createCar`` that
217+
will allow us to update the ledger and ultimately append a new block to the chain.
218+
But first, let's do another query.
219+
220+
Go back to the ``query.js`` program and edit the constructor request to query
221+
a specific car. We'll do this by changing the function from ``queryAllCars``
222+
to ``queryCar`` and passing a specific "Key" to the args parameter. Let's use
223+
``CAR4`` here. So our edited ``query.js`` program should now contain the
224+
following:
225+
226+
.. code:: bash
227+
228+
const request = {
229+
chaincodeId: options.chaincode_id,
230+
txId: transaction_id,
231+
fcn: 'queryCar',
232+
args: ['CAR4']
233+
234+
Save the program and navigate back to your ``fabcar`` directory. Now run the
235+
program again:
236+
237+
.. code:: bash
238+
239+
node query.js
240+
241+
You should see the following:
242+
243+
.. code:: json
244+
245+
{"colour":"black","make":"Tesla","model":"S","owner":"Adriana"}
246+
247+
So we've gone from querying all cars to querying just one, Adriana's black Tesla
248+
Model S. Using the ``queryCar`` function, we can query against any key (e.g. ``CAR0``) and
249+
get whatever make, model, color, and owner correspond to that car.
250+
251+
Great. Now you should be comfortable with the basic query functions in the chaincode,
252+
and the handful of parameters in the query program. Time to update the ledger...
253+
254+
Updating the Ledger
255+
-------------------
256+
257+
Now that we’ve done a few ledger queries and added a bit of code, we’re ready to
258+
update the ledger. There are a lot of potential updates we could
259+
make, but let's just create a new car for starters.
260+
261+
Ledger updates start with an application generating a transaction proposal.
262+
Just like query, a request is constructed to identify the channel ID,
263+
function, and specific smart contract to target for the transaction. The program
264+
then calls the ``channel.SendTransactionProposal`` API to send the transaction proposal to the peer(s)
265+
for endorsement.
266+
267+
The network (i.e. endorsing peer) returns a proposal response, which the application uses
268+
to build and sign a transaction request. This request is sent to the ordering service by
269+
calling the ``channel.sendTransaction`` API. The ordering service will bundle the transaction
270+
into a block and then "deliver" the block to all peers on a channel for validation. (In our
271+
case we have only the single endorsing peer.)
272+
273+
Finally the application uses the ``eh.setPeerAddr`` API to connect to the peer's
274+
event listener port, and calls ``eh.registerTxEvent`` to register events associated
275+
with a specific transaction ID. This API allows the application to know the fate of
276+
a transaction (i.e. successfully committed or unsuccessful). Think of it as a notification mechanism.
277+
278+
.. note:: We don't go into depth here on a transaction's lifecycle. Consult the
279+
:doc:`txflow` documentation for lower level details on how a transaction
280+
is ultimately committed to the ledger.
281+
282+
The goal with our initial invoke is to simply create a new asset (car in this case). We
283+
have a separate javascript program - ``invoke.js`` - that we will use for these transactions.
284+
Just like query, use an editor to open the program and navigate to the codeblock where we
285+
construct our invocation:
286+
287+
.. code:: bash
288+
289+
// createCar - requires 5 args, ex: args: ['CAR11', 'Honda', 'Accord', 'Black', 'Tom'],
290+
// changeCarOwner - requires 2 args , ex: args: ['CAR10', 'Barry'],
291+
// send proposal to endorser
292+
var request = {
293+
targets: targets,
294+
chaincodeId: options.chaincode_id,
295+
fcn: '',
296+
args: [''],
297+
chainId: options.channel_id,
298+
txId: tx_id
299+
300+
You'll see that we can call one of two functions - ``createCar`` or ``changeCarOwner``.
301+
Let's create a red Chevy Volt and give it to an owner named Nick. We're up to ``CAR9``
302+
on our ledger, so we'll use ``CAR10`` as the identifying key here. The updated codeblock
303+
should look like this:
304+
305+
.. code:: bash
306+
307+
var request = {
308+
targets: targets,
309+
chaincodeId: options.chaincode_id,
310+
fcn: 'createCar',
311+
args: ['CAR10', 'Chevy', 'Volt', 'Red', 'Nick'],
312+
chainId: options.channel_id,
313+
txId: tx_id
314+
315+
Save it and run the program:
316+
317+
.. code:: bash
318+
319+
node invoke.js
320+
321+
There will be some output in the terminal about Proposal Response and Transaction ID. However,
322+
all we're concerned with is this message:
323+
324+
.. code:: bash
325+
326+
The transaction has been committed on peer localhost:7053
327+
328+
The peer emits this event notification, and our application receives it thanks to our
329+
``eh.registerTxEvent`` API. So now if we go back to our ``query.js`` program and call
330+
the ``queryCar`` function against an arg of ``CAR10``, we should see the following:
331+
332+
.. code:: bash
333+
334+
Response is {"colour":"Red","make":"Chevy","model":"Volt","owner":"Nick"}
335+
336+
Finally, let's call our last function - ``changeCarOwner``. Nick is feeling generous and
337+
he wants to give his Chevy Volt to a man named Barry. So, we simply edit ``invoke.js``
338+
to reflect the following:
339+
340+
.. code:: bash
341+
342+
var request = {
343+
targets: targets,
344+
chaincodeId: options.chaincode_id,
345+
fcn: 'changeCarOwner',
346+
args: ['CAR10', 'Barry'],
347+
chainId: options.channel_id,
348+
txId: tx_id
349+
350+
Execute the program again - ``node invoke.js`` - and then run the query app one final time.
351+
We are still querying against ``CAR10``, so we should see:
352+
353+
.. code:: bash
354+
355+
Response is {"colour":"Red","make":"Chevy","model":"Volt","owner":"Barry"}
356+
357+
Additional Resources
358+
--------------------
359+
360+
The `Hyperledger Fabric Node SDK repo <https://github.com/hyperledger/fabric-sdk-node>`__
361+
is an excellent resource for deeper documentation and sample code. You can also consult
362+
the Fabric community and component experts on `Hyperledger Rocket Chat <https://chat.hyperledger.org/home>`__.
363+
364+
.. Licensed under Creative Commons Attribution 4.0 International License
365+
https://creativecommons.org/licenses/by/4.0/

0 commit comments

Comments
 (0)