github.com/darrenli6/fabric-sdk-example@v0.0.0-20220109053535-94b13b56df8c/docs/source/write_first_app.rst (about) 1 Writing Your First Application 2 ============================== 3 4 .. note:: If you're not yet familiar with the fundamental architecture of a 5 Fabric network, you may want to visit the :doc:`blockchain` and 6 :doc:`build_network` documentation prior to continuing. 7 8 In this section we'll be looking at a handful of sample programs to see how Fabric 9 apps work. These apps (and the smart contract they use) -- collectively known as 10 ``fabcar`` -- provide a broad demonstration of Fabric functionality. Notably, we 11 will show the process for interacting with a Certificate Authority and generating 12 enrollment certificates, after which we will leverage these generated identities 13 (user objects) to query and update a ledger. 14 15 We’ll go through three principle steps: 16 17 **1. Setting up a development environment.** Our application needs a network to 18 interact with, so we'll download one stripped down to just the components we need 19 for registration/enrollment, queries and updates: 20 21 .. image:: images/AppConceptsOverview.png 22 23 **2. Learning the parameters of the sample smart contract our app will use.** Our 24 smart contract contains various functions that allow us to interact with the ledger 25 in different ways. We’ll go in and inspect that smart contract to learn about the 26 functions our applications will be using. 27 28 **3. Developing the applications to be able to query and update assets on the ledger.** 29 We'll get into the app code itself (our apps have been written in Javascript) and 30 manually manipulate the variables to run different kinds of queries and updates. 31 32 After completing this tutorial you should have a basic understanding of how 33 an application is programmed in conjunction with a smart contract to interact 34 with the ledger (i.e. the peer) on a Fabric network. 35 36 Setting up your Dev Environment 37 ------------------------------- 38 39 First thing, let's download the Fabric images and the accompanying artifacts for the network 40 and applications... 41 42 Visit the :doc:`prereqs` page and ensure you have the necessary dependencies 43 installed on your machine. 44 45 Next, visit the :doc:`samples` page and follow the provided instructions. Return to 46 this tutorial once you have cloned the ``fabric-samples`` repository, and downloaded 47 the latest stable Fabric images and available utilities. 48 49 At this point everything should be installed. Navigate to the ``fabcar`` subdirectory 50 within your ``fabric-samples`` repository and take a look at what's inside: 51 52 .. code:: bash 53 54 cd fabric-samples/fabcar && ls 55 56 You should see the following: 57 58 .. code:: bash 59 60 enrollAdmin.js invoke.js package.json query.js registerUser.js startFabric.sh 61 62 Before starting we also need to do a little housekeeping. Run the following command to 63 kill any stale or active containers: 64 65 .. code:: bash 66 67 docker rm -f $(docker ps -aq) 68 69 Clear any cached networks: 70 71 .. code:: bash 72 73 # Press 'y' when prompted by the command 74 75 docker network prune 76 77 And lastly if you've already run through this tutorial, you'll also want to delete the 78 underlying chaincode image for the ``fabcar`` smart contract. If you're a user going through 79 this content for the first time, then you won't have this chaincode image on your system: 80 81 .. code:: bash 82 83 docker rmi dev-peer0.org1.example.com-fabcar-1.0-5c906e402ed29f20260ae42283216aa75549c571e2e380f3615826365d8269ba 84 85 Install the clients & launch the network 86 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 87 88 .. note:: The following instructions require you to be in the ``fabcar`` subdirectory 89 within your local clone of the ``fabric-samples`` repo. Remain at the 90 root of this subdirectory for the remainder of this tutorial. 91 92 Run the following command to install the Fabric dependencies for the applications. 93 We are concerned with ``fabric-ca-client`` which will allow our app(s) to communicate 94 with the CA server and retrieve identity material, and with ``fabric-client`` which 95 allows us to load the identity material and talk to the peers and ordering service. 96 97 .. code:: bash 98 99 npm install 100 101 Launch your network using the ``startFabric.sh`` shell script. This command 102 will spin up our various Fabric entities and launch a smart contract container for 103 chaincode written in Golang: 104 105 .. code:: bash 106 107 ./startFabric.sh 108 109 Alright, now that you’ve got a sample network and some code, let’s take a 110 look at how the different pieces fit together. 111 112 How Applications Interact with the Network 113 ------------------------------------------ 114 115 For a more in-depth look at the components in our ``fabcar`` network (and how 116 they're deployed) as well as how applications interact with those components 117 on more of a granular level, see :doc:`understand_fabcar_network`. 118 119 Developers more interested in seeing what applications **do** -- as well as 120 looking at the code itself to see how an application is constructed -- should 121 continue. For now, the most important thing to know is that applications use 122 a software development kit (SDK) to access the **APIs** that permit queries and 123 updates to the ledger. 124 125 Enrolling the Admin User 126 ------------------------ 127 128 .. note:: The following two sections involve communication with the Certificate 129 Authority. You may find it useful to stream the CA logs when running 130 the upcoming programs. 131 132 To stream your CA logs, split your terminal or open a new shell and issue the following: 133 134 .. code:: bash 135 136 docker logs -f ca.example.com 137 138 Now hop back to your terminal with the ``fabcar`` content... 139 140 When we launched our network, an admin user - ``admin`` - was registered with our 141 Certificate Authority. Now we need to send an enroll call to the CA server and 142 retrieve the enrollment certificate (eCert) for this user. We won't delve into enrollment 143 details here, but suffice it to say that the SDK and by extension our applications 144 need this cert in order to form a user object for the admin. We will then use this admin 145 object to subsequently register and enroll a new user. Send the admin enroll call to the CA 146 server: 147 148 .. code:: bash 149 150 node enrollAdmin.js 151 152 This program will invoke a certificate signing request (CSR) and ultimately output 153 an eCert and key material into a newly created folder - ``hfc-key-store`` - at the 154 root of this project. Our apps will then look to this location when they need to 155 create or load the identity objects for our various users. 156 157 Register and Enroll ``user1`` 158 ----------------------------- 159 160 With our newly generated admin eCert, we will now communicate with the CA server 161 once more to register and enroll a new user. This user - ``user1`` - will be 162 the identity we use when querying and updating the ledger. It's important to 163 note here that it is the ``admin`` identity that is issuing the registration and 164 enrollment calls for our new user (i.e. this user is acting in the role of a registrar). 165 Send the register and enroll calls for ``user1``: 166 167 .. code:: bash 168 169 node registerUser.js 170 171 Similar to the admin enrollment, this program invokes a CSR and outputs the keys 172 and eCert into the ``hfc-key-store`` subdirectory. So now we have identity material for two 173 separate users - ``admin`` & ``user1``. Time to interact with the ledger... 174 175 Querying the Ledger 176 ------------------- 177 178 Queries are how you read data from the ledger. This data is stored as a series 179 of key/value pairs, and you can query for the value of a single key, multiple 180 keys, or -- if the ledger is written in a rich data storage format like JSON -- 181 perform complex searches against it (looking for all assets that contain 182 certain keywords, for example). 183 184 This is a representation of how a query works: 185 186 .. image:: images/QueryingtheLedger.png 187 188 First, let's run our ``query.js`` program to return a listing of all the cars on 189 the ledger. We will use our second identity - ``user1`` - as the signing entity 190 for this application. The following line in our program specifies ``user1`` as 191 the signer: 192 193 .. code:: bash 194 195 fabric_client.getUserContext('user1', true); 196 197 Recall that the ``user1`` enrollment material has already been placed into our 198 ``hfc-key-store`` subdirectory, so we simply need to tell our application to grab that identity. 199 With the user object defined, we can now proceed with reading from the ledger. 200 A function that will query all the cars, ``queryAllCars``, is 201 pre-loaded in the app, so we can simply run the program as is: 202 203 .. code:: bash 204 205 node query.js 206 207 It should return something like this: 208 209 .. code:: json 210 211 Query result count = 1 212 Response is [{"Key":"CAR0", "Record":{"colour":"blue","make":"Toyota","model":"Prius","owner":"Tomoko"}}, 213 {"Key":"CAR1", "Record":{"colour":"red","make":"Ford","model":"Mustang","owner":"Brad"}}, 214 {"Key":"CAR2", "Record":{"colour":"green","make":"Hyundai","model":"Tucson","owner":"Jin Soo"}}, 215 {"Key":"CAR3", "Record":{"colour":"yellow","make":"Volkswagen","model":"Passat","owner":"Max"}}, 216 {"Key":"CAR4", "Record":{"colour":"black","make":"Tesla","model":"S","owner":"Adriana"}}, 217 {"Key":"CAR5", "Record":{"colour":"purple","make":"Peugeot","model":"205","owner":"Michel"}}, 218 {"Key":"CAR6", "Record":{"colour":"white","make":"Chery","model":"S22L","owner":"Aarav"}}, 219 {"Key":"CAR7", "Record":{"colour":"violet","make":"Fiat","model":"Punto","owner":"Pari"}}, 220 {"Key":"CAR8", "Record":{"colour":"indigo","make":"Tata","model":"Nano","owner":"Valeria"}}, 221 {"Key":"CAR9", "Record":{"colour":"brown","make":"Holden","model":"Barina","owner":"Shotaro"}}] 222 223 These are the 10 cars. A black Tesla Model S owned by Adriana, a red Ford Mustang 224 owned by Brad, a violet Fiat Punto owned by Pari, and so on. The ledger is 225 key/value based and in our implementation the key is ``CAR0`` through ``CAR9``. 226 This will become particularly important in a moment. 227 228 Let's take a closer look at this program. Use an editor (e.g. atom or visual studio) 229 and open ``query.js``. 230 231 The initial section of the application defines certain variables such as 232 channel name, cert store location and network endpoints. In our sample app, these 233 variables have been baked-in, but in a real app these variables would have to 234 be specified by the app dev. 235 236 .. code:: bash 237 238 var channel = fabric_client.newChannel('mychannel'); 239 var peer = fabric_client.newPeer('grpc://localhost:7051'); 240 channel.addPeer(peer); 241 242 var member_user = null; 243 var store_path = path.join(__dirname, 'hfc-key-store'); 244 console.log('Store path:'+store_path); 245 var tx_id = null; 246 247 This is the chunk where we construct our query: 248 249 .. code:: bash 250 251 // queryCar chaincode function - requires 1 argument, ex: args: ['CAR4'], 252 // queryAllCars chaincode function - requires no arguments , ex: args: [''], 253 const request = { 254 //targets : --- letting this default to the peers assigned to the channel 255 chaincodeId: 'fabcar', 256 fcn: 'queryAllCars', 257 args: [''] 258 }; 259 260 When the application ran, it invoked the ``fabcar`` chaincode on the peer, ran the 261 ``queryAllCars`` function within it, and passed no arguments to it. 262 263 To take a look at the available functions within our smart contract, navigate 264 to the ``chaincode/fabcar/go`` subdirectory at the root of ``fabric-samples`` and open 265 ``fabcar.go`` in your editor. 266 267 .. note:: These same functions are defined within the Node.js version of the 268 ``fabcar`` chaincode. 269 270 You'll see that we have the following functions available to call: ``initLedger``, 271 ``queryCar``, ``queryAllCars``, ``createCar``, and ``changeCarOwner``. 272 273 Let's take a closer look at the ``queryAllCars`` function to see how it 274 interacts with the ledger. 275 276 .. code:: bash 277 278 func (s *SmartContract) queryAllCars(APIstub shim.ChaincodeStubInterface) sc.Response { 279 280 startKey := "CAR0" 281 endKey := "CAR999" 282 283 resultsIterator, err := APIstub.GetStateByRange(startKey, endKey) 284 285 This defines the range of ``queryAllCars``. Every car between ``CAR0`` and 286 ``CAR999`` -- 1,000 cars in all, assuming every key has been tagged properly 287 -- will be returned by the query. 288 289 Below is a representation of how an app would call different functions in 290 chaincode. Each function must be coded against an available API in the chaincode 291 shim interface, which in turn allows the smart contract container to properly 292 interface with the peer ledger. 293 294 .. image:: images/RunningtheSample.png 295 296 We can see our ``queryAllCars`` function, as well as one called ``createCar``, 297 that will allow us to update the ledger and ultimately append a new block to 298 the chain in a moment. 299 300 But first, go back to the ``query.js`` program and edit the constructor request 301 to query ``CAR4``. We do this by changing the function in ``query.js`` from 302 ``queryAllCars`` to ``queryCar`` and passing ``CAR4`` as the specific key. 303 304 The ``query.js`` program should now look like this: 305 306 .. code:: bash 307 308 const request = { 309 //targets : --- letting this default to the peers assigned to the channel 310 chaincodeId: 'fabcar', 311 fcn: 'queryCar', 312 args: ['CAR4'] 313 }; 314 315 Save the program and navigate back to your ``fabcar`` directory. Now run the 316 program again: 317 318 .. code:: bash 319 320 node query.js 321 322 You should see the following: 323 324 .. code:: json 325 326 {"colour":"black","make":"Tesla","model":"S","owner":"Adriana"} 327 328 If you go back and look at the result from when we queried every car before, 329 you can see that ``CAR4`` was Adriana’s black Tesla model S, which is the result 330 that was returned here. 331 332 Using the ``queryCar`` function, we can query against any key (e.g. ``CAR0``) 333 and get whatever make, model, color, and owner correspond to that car. 334 335 Great. At this point you should be comfortable with the basic query functions 336 in the smart contract and the handful of parameters in the query program. 337 Time to update the ledger... 338 339 Updating the Ledger 340 ------------------- 341 342 Now that we’ve done a few ledger queries and added a bit of code, we’re ready to 343 update the ledger. There are a lot of potential updates we could make, but 344 let's start by creating a car. 345 346 Below we can see how this process works. An update is proposed, endorsed, 347 then returned to the application, which in turn sends it to be ordered and 348 written to every peer's ledger: 349 350 .. image:: images/UpdatingtheLedger.png 351 352 Our first update to the ledger will be to create a new car. We have a separate 353 Javascript program -- ``invoke.js`` -- that we will use to make updates. Just 354 as with queries, use an editor to open the program and navigate to the 355 code block where we construct our invocation: 356 357 .. code:: bash 358 359 // createCar chaincode function - requires 5 args, ex: args: ['CAR12', 'Honda', 'Accord', 'Black', 'Tom'], 360 // changeCarOwner chaincode function - requires 2 args , ex: args: ['CAR10', 'Barry'], 361 // must send the proposal to endorsing peers 362 var request = { 363 //targets: let default to the peer assigned to the client 364 chaincodeId: 'fabcar', 365 fcn: '', 366 args: [''], 367 chainId: 'mychannel', 368 txId: tx_id 369 }; 370 371 You'll see that we can call one of two functions - ``createCar`` or 372 ``changeCarOwner``. First, let’s create a red Chevy Volt and give it to an 373 owner named Nick. We're up to ``CAR9`` on our ledger, so we'll use ``CAR10`` 374 as the identifying key here. Edit this code block to look like this: 375 376 .. code:: bash 377 378 var request = { 379 //targets: let default to the peer assigned to the client 380 chaincodeId: 'fabcar', 381 fcn: 'createCar', 382 args: ['CAR10', 'Chevy', 'Volt', 'Red', 'Nick'], 383 chainId: 'mychannel', 384 txId: tx_id 385 }; 386 387 Save it and run the program: 388 389 .. code:: bash 390 391 node invoke.js 392 393 There will be some output in the terminal about ``ProposalResponse`` and 394 promises. However, all we're concerned with is this message: 395 396 .. code:: bash 397 398 The transaction has been committed on peer localhost:7053 399 400 To see that this transaction has been written, go back to ``query.js`` and 401 change the argument from ``CAR4`` to ``CAR10``. 402 403 In other words, change this: 404 405 .. code:: bash 406 407 const request = { 408 //targets : --- letting this default to the peers assigned to the channel 409 chaincodeId: 'fabcar', 410 fcn: 'queryCar', 411 args: ['CAR4'] 412 }; 413 414 To this: 415 416 .. code:: bash 417 418 const request = { 419 //targets : --- letting this default to the peers assigned to the channel 420 chaincodeId: 'fabcar', 421 fcn: 'queryCar', 422 args: ['CAR10'] 423 }; 424 425 Save once again, then query: 426 427 .. code:: bash 428 429 node query.js 430 431 Which should return this: 432 433 .. code:: bash 434 435 Response is {"colour":"Red","make":"Chevy","model":"Volt","owner":"Nick"} 436 437 Congratulations. You’ve created a car! 438 439 So now that we’ve done that, let’s say that Nick is feeling generous and he 440 wants to give his Chevy Volt to someone named Dave. 441 442 To do this go back to ``invoke.js`` and change the function from ``createCar`` 443 to ``changeCarOwner`` and input the arguments like this: 444 445 .. code:: bash 446 447 var request = { 448 //targets: let default to the peer assigned to the client 449 chaincodeId: 'fabcar', 450 fcn: 'changeCarOwner', 451 args: ['CAR10', 'Dave'], 452 chainId: 'mychannel', 453 txId: tx_id 454 }; 455 456 The first argument -- ``CAR10`` -- reflects the car that will be changing 457 owners. The second argument -- ``Dave`` -- defines the new owner of the car. 458 459 Save and execute the program again: 460 461 .. code:: bash 462 463 node invoke.js 464 465 Now let’s query the ledger again and ensure that Dave is now associated with the 466 ``CAR10`` key: 467 468 .. code:: bash 469 470 node query.js 471 472 It should return this result: 473 474 .. code:: bash 475 476 Response is {"colour":"Red","make":"Chevy","model":"Volt","owner":"Dave"} 477 478 The ownership of ``CAR10`` has been changed from Nick to Dave. 479 480 .. note:: In a real world application the chaincode would likely have some access 481 control logic. For example, only certain authorized users may create 482 new cars, and only the car owner may transfer the car to somebody else. 483 484 Summary 485 ------- 486 487 Now that we’ve done a few queries and a few updates, you should have a pretty 488 good sense of how applications interact with the network. You’ve seen the basics 489 of the roles smart contracts, APIs, and the SDK play in queries and updates and 490 you should have a feel for how different kinds of applications could be used to 491 perform other business tasks and operations. 492 493 In subsequent documents we’ll learn how to actually **write** a smart contract 494 and how some of these more low level application functions can be leveraged 495 (especially relating to identity and membership services). 496 497 Additional Resources 498 -------------------- 499 500 The `Hyperledger Fabric Node SDK repo <https://github.com/hyperledger/fabric-sdk-node>`__ 501 is an excellent resource for deeper documentation and sample code. You can also consult 502 the Fabric community and component experts on `Hyperledger Rocket Chat <https://chat.hyperledger.org/home>`__. 503 504 .. Licensed under Creative Commons Attribution 4.0 International License 505 https://creativecommons.org/licenses/by/4.0/