github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/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:`key_concepts` section 6 prior to continuing. 7 8 It is also worth noting that this tutorial serves as an introduction 9 to Fabric applications and uses simple smart contracts and 10 applications. For a more in-depth look at Fabric applications and 11 smart contracts, check out our 12 :doc:`developapps/developing_applications` section or the 13 :doc:`tutorial/commercial_paper`. 14 15 This tutorial provides an introduction to how Fabric applications interact 16 with deployed blockchain networks. The tutorial uses sample programs built using the 17 Fabric SDKs -- described in detail in the :doc:`/developapps/application` topic -- 18 to invoke a smart contract which queries and updates the ledger with the smart 19 contract API -- described in detail in :doc:`/developapps/smartcontract`. 20 We will also use our sample programs and a deployed Certificate Authority to generate 21 the X.509 certificates that an application needs to interact with a permissioned 22 blockchain. 23 24 **About Asset Transfer** 25 26 This Asset Transfer (basic) sample demonstrates how to initialize a ledger with assets, query those assets, create 27 a new asset, query a single asset based on an asset ID, update an existing asset, and transfer an asset to a new owner. 28 It involves the following two components: 29 30 1. Sample application: which makes calls to the blockchain network, invoking transactions 31 implemented in the chaincode (smart contract). The application is located in the following ``fabric-samples`` directory: 32 33 .. code:: bash 34 35 asset-transfer-basic/application-javascript 36 37 2. Smart contract itself, implementing the transactions that involve interactions with the 38 ledger. The smart contract (chaincode) is located in the following ``fabric-samples`` directory: 39 40 .. code:: bash 41 42 asset-transfer-basic/chaincode-(javascript, java, go, typescript) 43 44 Please note that for the purposes of this tutorial, the terms chaincode and smart contract are used 45 interchangeably. For this example, we will be using the javascript chaincode. 46 47 We’ll go through three principle steps: 48 49 **1. Setting up a development environment.** Our application needs a network 50 to interact with, so we'll deploy a basic network for our smart contracts and 51 application. 52 53 .. image:: images/AppConceptsOverview.png 54 55 **2. Explore a sample smart contract.** 56 We’ll inspect the sample assetTransfer (javascript) smart contract to learn about the transactions within it, 57 and how they are used by an application to query and update the ledger. 58 59 **3. Interact with the smart contract with a sample application.** Our application will 60 use the assetTransfer smart contract to create, query, and update assets on the ledger. 61 We'll get into the code of the app and the transactions they create, including initializing 62 the ledger with assets, querying an asset, querying a range of assets, creating a new asset, 63 and transferring an asset to a new owner. 64 65 After completing this tutorial you should have a basic understanding of how Fabric 66 applications and smart contracts work together to manage data on the distributed 67 ledger of a blockchain network. 68 69 Before you begin 70 ---------------- 71 72 In addition to the standard :doc:`prereqs` for Fabric, this tutorial leverages the Hyperledger Fabric SDK for Node.js. See the Node.js SDK `README <https://github.com/hyperledger/fabric-sdk-node#build-and-test>`__ for a up to date list of prerequisites. 73 74 - If you are using macOS, complete the following steps: 75 76 1. Install `Homebrew <https://brew.sh/>`_. 77 2. Check the Node SDK `prerequisites <https://github.com/hyperledger/fabric-sdk-node#build-and-test>`_ to find out what level of Node to install. 78 3. Run ``brew install node`` to download the latest version of node or choose a specific version, for example: ``brew install node@10`` according to what is supported in the prerequisites. 79 4. Run ``npm install``. 80 81 - If you are on Windows, you can install the `windows-build-tools <https://github.com/felixrieseberg/windows-build-tools#readme>`_ with npm which installs all required compilers and tooling by running the following command: 82 83 .. code:: bash 84 85 npm install --global windows-build-tools 86 87 - If you are on Linux, you need to install `Python v2.7 <https://www.python.org/download/releases/2.7/>`_, `make <https://www.gnu.org/software/make/>`_, and a C/C++ compiler toolchain such as `GCC <https://gcc.gnu.org/>`_. You can run the following command to install the other tools: 88 89 .. code:: bash 90 91 sudo apt install build-essential 92 93 Set up the blockchain network 94 ----------------------------- 95 96 If you've already run through :doc:`test_network` tutorial and have a network up 97 and running, this tutorial will bring down your running network before 98 bringing up a new one. 99 100 101 Launch the network 102 ^^^^^^^^^^^^^^^^^^ 103 104 .. note:: This tutorial demonstrates the JavaScript versions of the Asset Transfer 105 smart contract and application, but the ``fabric-samples`` repository also 106 contains Go, Java and TypeScript versions of this sample smart contract. To try the 107 Go, Java or TypeScript versions, change the ``javascript`` argument 108 for ``./network.sh deployCC -ccl javascript`` below to either ``go``, ``java`` or ``typescript`` 109 and follow the instructions written to the terminal. You may use any chaincode language sample with 110 the javascript application sample (e.g javascript application calling go chaincode functions or 111 javascript application calling typescript chaincode functions, etc.) 112 113 Navigate to the ``test-network`` subdirectory within your local clone of the 114 ``fabric-samples`` repository. 115 116 .. code:: bash 117 118 cd fabric-samples/test-network 119 120 If you already have a test network running, bring it down to ensure the environment is clean. 121 122 .. code:: bash 123 124 ./network.sh down 125 126 Launch the Fabric test network using the ``network.sh`` shell script. 127 128 .. code:: bash 129 130 ./network.sh up createChannel -c mychannel -ca 131 132 This command will deploy the Fabric test network with two peers, an ordering service, and three certificate authorities (Orderer, Org1, Org2). 133 Instead of using the cryptogen tool, we bring up the test network using Certificate Authorities, 134 hence the ``-ca`` flag. Additionally, the org admin user registration is bootstrapped when the Certificate Authority is started. 135 In a later step, we will show how the sample application completes the admin enrollment. 136 137 Next, let's deploy the chaincode by calling the ``./network.sh`` script with the chaincode name and language options. 138 139 .. code:: bash 140 141 ./network.sh deployCC -ccn basic -ccp ../asset-transfer-basic/chaincode-javascript/ -ccl javascript 142 143 .. note:: Behind the scenes, this script uses the chaincode lifecycle to package, install, 144 query installed chaincode, approve chaincode for both Org1 and Org2, and finally commit the chaincode. 145 146 If the chaincode is successfully deployed, the end of the output in your terminal should look similar to below: 147 148 .. code:: bash 149 150 Committed chaincode definition for chaincode 'basic' on channel 'mychannel': 151 Version: 1.0, Sequence: 1, Endorsement Plugin: escc, Validation Plugin: vscc, Approvals: [Org1MSP: true, Org2MSP: true] 152 ===================== Query chaincode definition successful on peer0.org2 on channel 'mychannel' ===================== 153 154 ===================== Chaincode initialization is not required ===================== 155 156 157 Sample application 158 ^^^^^^^^^^^^^^^^^^ 159 Next, let's prepare the sample Asset Transfer Javascript application that will be used to interact with the deployed chaincode. 160 161 - `JavaScript application <https://github.com/hyperledger/fabric-samples/blob/master/asset-transfer-basic/application-javascript>`__ 162 163 Note that the sample application is also available in Go and Java at the links below: 164 165 - `Go application <https://github.com/hyperledger/fabric-samples/blob/master/asset-transfer-basic/application-go>`__ 166 - `Java application <https://github.com/hyperledger/fabric-samples/blob/master/asset-transfer-basic/application-java>`__ 167 168 169 Open a new terminal, and navigate to the ``application-javascript`` folder. 170 171 .. code:: bash 172 173 cd asset-transfer-basic/application-javascript 174 175 This directory contains sample programs that were developed using the Fabric 176 SDK for Node.js. Run the following command to install the application dependencies. 177 It may take up to a minute to complete: 178 179 .. code:: bash 180 181 npm install 182 183 This process is installing the key application dependencies defined in the application's 184 ``package.json``. The most important of which is the ``fabric-network`` Node.js module; 185 it enables an application to use identities, wallets, and gateways to connect to 186 channels, submit transactions, and wait for notifications. This tutorial also 187 uses the ``fabric-ca-client`` module to enroll users with their respective 188 certificate authorities, generating a valid identity which is then used by 189 the ``fabric-network`` module to interact with the blockchain network. 190 191 Once ``npm install`` completes, everything is in place to run the application. 192 Let's take a look at the sample JavaScript application files we will be using 193 in this tutorial. Run the following command to list the files in this directory: 194 195 .. code:: bash 196 197 ls 198 199 You should see the following: 200 201 .. code:: bash 202 203 app.js node_modules package.json package-lock.json 204 205 .. note:: The first part of the following section involves communication with the Certificate 206 Authority. You may find it useful to stream the CA logs when running 207 the upcoming programs by opening a new terminal shell and running 208 ``docker logs -f ca_org1``. 209 210 When we started the Fabric test network back in the first step, an admin user --- literally called ``admin`` --- 211 was created as the **registrar** for the Certificate Authority (CA). Our first 212 step is to generate the private key, public key, and X.509 certificate for 213 ``admin`` by having the application call the ``enrollAdmin`` . This process uses a **Certificate 214 Signing Request** (CSR) --- the private and public key are first generated 215 locally and the public key is then sent to the CA which returns an encoded 216 certificate for use by the application. These credentials are then stored 217 in the wallet, allowing us to act as an administrator for the CA. 218 219 Let's run the application and then step through each of the interactions with the smart contract functions. From the 220 ``asset-transfer-basic/application-javascript`` directory, run the following command: 221 222 .. code:: bash 223 224 node app.js 225 226 227 First, the application enrolls the admin user 228 --------------------------------------------- 229 230 .. note:: It is important to note that enrolling the admin and registering the app user are interactions that 231 take place between the application and the Certificate Authority, not between the application and the chaincode. 232 If you examine the chaincode in ``asset-transfer-basic/chaincode-javascript/lib`` you will find that the chaincode 233 does not contain any functionality that supports enrolling the admin or registering the user. 234 235 In the sample application code below, you will see that after getting reference to the 236 common connection profile path, making sure the connection profile exists, and specifying where to create the wallet, 237 ``enrollAdmin()`` is executed and the admin credentials are generated from the Certificate Authority. 238 239 .. code:: bash 240 241 async function main() { 242 try { 243 // build an in memory object with the network configuration (also known as a connection profile) 244 const ccp = buildCCP(); 245 246 // build an instance of the fabric ca services client based on 247 // the information in the network configuration 248 const caClient = buildCAClient(FabricCAServices, ccp); 249 250 // setup the wallet to hold the credentials of the application user 251 const wallet = await buildWallet(Wallets, walletPath); 252 253 // in a real application this would be done on an administrative flow, and only once 254 await enrollAdmin(caClient, wallet); 255 256 This command stores the CA administrator's credentials in the ``wallet`` directory. 257 You can find administrator's certificate and private key in the ``wallet/admin.id`` 258 file. 259 260 .. note:: If you decide to start over by taking down the network and bringing it back up again, you will 261 have to delete the ``wallet`` folder and its identities prior to re-running the javascript application 262 or you will get an error. This happens because the Certificate Authority and its database are taken down 263 when the test-network is taken down but the original wallet still remains in the application-javascript directory 264 so it must be deleted. When you re-run the sample javascript application, a new wallet and credentials will 265 be generated. 266 267 268 If you scroll back up to the beginning of the output in your terminal, it should be similar to below: 269 270 .. code:: bash 271 272 Wallet path: /Users/<your_username>/fabric-samples/asset-transfer-basic/application-javascript/wallet 273 Successfully enrolled admin user and imported it into the wallet 274 275 Because the admin registration step is bootstrapped when the Certificate Authority 276 is started, we only need to enroll the admin. 277 278 .. note:: Since the Fabric CA interactions are common across the samples, enrollAdmin() and the other CA 279 related functions are included in the ``fabric-samples/test-application/javascript/CAUtil.js`` 280 common utility. 281 282 As for the app user, we need the application to register and enroll the user in the next step. 283 284 285 Second, the application registers and enrolls an application user 286 ----------------------------------------------------------------- 287 288 Now that we have the administrator's credentials in a wallet, the application uses the ``admin`` 289 user to register and enroll an app user which will be used 290 to interact with the blockchain network. The section of the application code is shown below. 291 292 .. code:: bash 293 294 // in a real application this would be done only when a new user was required to be added 295 // and would be part of an administrative flow 296 await registerUser(caClient, wallet, userId, 'org1.department1'); 297 298 Similar to the admin enrollment, this function uses a CSR to register and enroll ``appUser`` and 299 store its credentials alongside those of ``admin`` in the wallet. We now have 300 identities for two separate users --- ``admin`` and ``appUser`` --- that can be 301 used by our application. 302 303 Scrolling further down in your terminal output, you should see confirmation of the app user registration 304 similar to this: 305 306 .. code:: bash 307 308 Successfully registered and enrolled user appUser and imported it into the wallet 309 310 Third, the sample application prepares a connection to the channel and smart contract 311 ------------------------------------------------------------------------------------- 312 313 In the prior steps, the application generated the admin and app user credentials and placed them in the wallet. 314 If the credentials exist and have the correct permissions attributes associated with them, the sample application user 315 will be able to call chaincode functions after getting reference to the channel name and contract name. 316 317 .. note:: Our connection configuration specifies only the peer from your own Org. 318 We tell node client sdk to use the service discovery (running on the peer), 319 which fetches other peers that are currently online, metadata like relevant endorsement policies 320 and any static information it would have otherwise needed to communicate with the rest of the nodes. 321 The ``asLocalhost`` set to ``true`` tells it to connect as localhost, since our client is running on same network as the other fabric nodes. 322 In deployments where you are not running the client on the same network as the other fabric nodes, 323 the ``asLocalhost`` option would be set to ``false``. 324 325 You will notice that in the following lines of application code, the application is getting reference 326 to the Contract using the contract name and channel name via Gateway: 327 328 .. code:: bash 329 330 // Create a new gateway instance for interacting with the fabric network. 331 // In a real application this would be done as the backend server session is setup for 332 // a user that has been verified. 333 const gateway = new Gateway(); 334 335 try { 336 // setup the gateway instance 337 // The user will now be able to create connections to the fabric network and be able to 338 // submit transactions and query. All transactions submitted by this gateway will be 339 // signed by this user using the credentials stored in the wallet. 340 await gateway.connect(ccp, { 341 wallet, 342 identity: userId, 343 discovery: {enabled: true, asLocalhost: true} // using asLocalhost as this gateway is using a fabric network deployed locally 344 }); 345 346 // Build a network instance based on the channel where the smart contract is deployed 347 const network = await gateway.getNetwork(channelName); 348 349 350 // Get the contract from the network. 351 const contract = network.getContract(chaincodeName); 352 353 When a chaincode package includes multiple smart contracts, on the `getContract() API <https://hyperledger.github.io/fabric-sdk-node/release-2.2/module-fabric-network.Network.html#getContract>`__ you can specify both the name of the chaincode package and a specific smart contract to target. For example: 354 355 .. code:: bash 356 357 const contract = await network.getContract('chaincodeName', 'smartContractName'); 358 359 Fourth, the application initializes the ledger with some sample data 360 -------------------------------------------------------------------- 361 362 Now that we are at the point where we are actually having the sample application submit transactions, let’s 363 go through them in sequence. The application code snippets and invoked chaincode snippets 364 are provided for each called function, as well as the terminal output. 365 366 The submitTransaction() function is used to invoke the chaincode ``InitLedger`` function to populate the 367 ledger with some sample data. Under the covers, the submitTransaction() function will use service discovery 368 to find a set of required endorsing peers for the chaincode, invoke the chaincode 369 on the required number of peers, gather the chaincode endorsed results from those peers, 370 and finally submit the transaction to the ordering service. 371 372 Sample application ``'InitLedger'`` call 373 374 .. code:: bash 375 376 // Initialize a set of asset data on the channel using the chaincode 'InitLedger' function. 377 // This type of transaction would only be run once by an application the first time it was started after it 378 // deployed the first time. Any updates to the chaincode deployed later would likely not need to run 379 // an "init" type function. 380 console.log('\n--> Submit Transaction: InitLedger, function creates the initial set of assets on the ledger'); 381 await contract.submitTransaction('InitLedger'); 382 console.log('*** Result: committed'); 383 384 Chaincode ``'InitLedger'`` function 385 386 .. code:: bash 387 388 async InitLedger(ctx) { 389 const assets = [ 390 { 391 ID: 'asset1', 392 Color: 'blue', 393 Size: 5, 394 Owner: 'Tomoko', 395 AppraisedValue: 300, 396 }, 397 { 398 ID: 'asset2', 399 Color: 'red', 400 Size: 5, 401 Owner: 'Brad', 402 AppraisedValue: 400, 403 }, 404 { 405 ID: 'asset3', 406 Color: 'green', 407 Size: 10, 408 Owner: 'Jin Soo', 409 AppraisedValue: 500, 410 }, 411 { 412 ID: 'asset4', 413 Color: 'yellow', 414 Size: 10, 415 Owner: 'Max', 416 AppraisedValue: 600, 417 }, 418 { 419 ID: 'asset5', 420 Color: 'black', 421 Size: 15, 422 Owner: 'Adriana', 423 AppraisedValue: 700, 424 }, 425 { 426 ID: 'asset6', 427 Color: 'white', 428 Size: 15, 429 Owner: 'Michel', 430 AppraisedValue: 800, 431 }, 432 ]; 433 434 for (const asset of assets) { 435 asset.docType = 'asset'; 436 await ctx.stub.putState(asset.ID, Buffer.from(JSON.stringify(asset))); 437 console.info(`Asset ${asset.ID} initialized`); 438 } 439 } 440 441 The terminal output entry should look similar to below: 442 443 .. code:: bash 444 445 Submit Transaction: InitLedger, function creates the initial set of assets on the ledger 446 447 448 Fifth, the application invokes each of the chaincode functions 449 -------------------------------------------------------------- 450 451 First, a word about querying the ledger. 452 453 Each peer in a blockchain network hosts a copy of the `ledger <./ledger/ledger.html>`_. An application 454 program can view the most recent data from the ledger using read-only invocations of 455 a smart contract running on your peers called a query. 456 457 Here is a simplified representation of how a query works: 458 459 .. image:: tutorial/write_first_app.diagram.1.png 460 461 The most common queries involve the current values of data in the ledger -- its 462 `world state <./ledger/ledger.html#world-state>`_. The world state is 463 represented as a set of key-value pairs, and applications can query data for a 464 single key or multiple keys. Moreover, you can use complex queries to read the 465 data on the ledger when you use CouchDB as your state database and model your data in JSON. 466 This can be very helpful when looking for all assets that match certain keywords 467 with particular values; all assets with a particular owner, for example. 468 469 Below, the sample application is just getting all the assets that we populated in the prior 470 step when we initialized the ledger with data. The evaluateTransaction() function is 471 used when you'd like to query a single peer, without submitting a transaction to 472 the ordering service. 473 474 Sample application ``'GetAllAssets'`` call 475 476 .. code:: bash 477 478 // Let's try a query type operation (function). 479 // This will be sent to just one peer and the results will be shown. 480 console.log('\n--> Evaluate Transaction: GetAllAssets, function returns all the current assets on the ledger'); 481 let result = await contract.evaluateTransaction('GetAllAssets'); 482 console.log(`*** Result: ${prettyJSONString(result.toString())}`); 483 484 Chaincode ``'GetAllAssets'`` function 485 486 .. code:: bash 487 488 // GetAllAssets returns all assets found in the world state. 489 async GetAllAssets(ctx) { 490 const allResults = []; 491 // range query with empty string for startKey and endKey does an open-ended query of all assets in the chaincode namespace. 492 const iterator = await ctx.stub.getStateByRange('', ''); 493 let result = await iterator.next(); 494 while (!result.done) { 495 const strValue = Buffer.from(result.value.value.toString()).toString('utf8'); 496 let record; 497 try { 498 record = JSON.parse(strValue); 499 } catch (err) { 500 console.log(err); 501 record = strValue; 502 } 503 allResults.push({ Key: result.value.key, Record: record }); 504 result = await iterator.next(); 505 } 506 return JSON.stringify(allResults); 507 } 508 509 The terminal output should look like this: 510 511 .. code:: json 512 513 Evaluate Transaction: GetAllAssets, function returns all the current assets on the ledger 514 Result: [ 515 { 516 "Key": "asset1", 517 "Record": { 518 "ID": "asset1", 519 "Color": "blue", 520 "Size": 5, 521 "Owner": "Tomoko", 522 "AppraisedValue": 300, 523 "docType": "asset" 524 } 525 }, 526 { 527 "Key": "asset2", 528 "Record": { 529 "ID": "asset2", 530 "Color": "red", 531 "Size": 5, 532 "Owner": "Brad", 533 "AppraisedValue": 400, 534 "docType": "asset" 535 } 536 }, 537 { 538 "Key": "asset3", 539 "Record": { 540 "ID": "asset3", 541 "Color": "green", 542 "Size": 10, 543 "Owner": "Jin Soo", 544 "AppraisedValue": 500, 545 "docType": "asset" 546 } 547 }, 548 { 549 "Key": "asset4", 550 "Record": { 551 "ID": "asset4", 552 "Color": "yellow", 553 "Size": 10, 554 "Owner": "Max", 555 "AppraisedValue": 600, 556 "docType": "asset" 557 } 558 }, 559 { 560 "Key": "asset5", 561 "Record": { 562 "ID": "asset5", 563 "Color": "black", 564 "Size": 15, 565 "Owner": "Adriana", 566 "AppraisedValue": 700, 567 "docType": "asset" 568 } 569 }, 570 { 571 "Key": "asset6", 572 "Record": { 573 "ID": "asset6", 574 "Color": "white", 575 "Size": 15, 576 "Owner": "Michel", 577 "AppraisedValue": 800, 578 "docType": "asset" 579 } 580 } 581 ] 582 583 Next, the sample application submits a transaction to create 'asset13'. 584 585 Sample application ``'CreateAsset'`` call 586 587 .. code:: bash 588 589 // Now let's try to submit a transaction. 590 // This will be sent to both peers and if both peers endorse the transaction, the endorsed proposal will be sent 591 // to the orderer to be committed by each of the peer's to the channel ledger. 592 console.log('\n--> Submit Transaction: CreateAsset, creates new asset with ID, color, owner, size, and appraisedValue arguments'); 593 await contract.submitTransaction('CreateAsset', 'asset13', 'yellow', '5', 'Tom', '1300'); 594 console.log('*** Result: committed'); 595 596 Chaincode ``'CreateAsset'`` function 597 598 .. code:: bash 599 600 // CreateAsset issues a new asset to the world state with given details. 601 async CreateAsset(ctx, id, color, size, owner, appraisedValue) { 602 const asset = { 603 ID: id, 604 Color: color, 605 Size: size, 606 Owner: owner, 607 AppraisedValue: appraisedValue, 608 }; 609 return ctx.stub.putState(id, Buffer.from(JSON.stringify(asset))); 610 } 611 612 Terminal output: 613 614 .. code:: bash 615 616 Submit Transaction: CreateAsset, creates new asset with ID, color, owner, size, and appraisedValue arguments 617 618 .. note:: In the application and chaincode snippets above, it is important 619 to note that the sample application submits the ``'CreateAsset'`` 620 transaction with the same type and number of arguments the chaincode 621 is expecting, and in the correct sequence. In this case, the transaction 622 name and correctly sequenced arguments are: ``'CreateAsset'``, ``'asset13'``, 623 ``'yellow'``, ``'5'``, ``'Tom'``, ``'1300'`` because the corresponding chaincode 624 CreateAsset is expecting the correct sequence and type of arguments that define the asset object: 625 sequence: ID, Color, Size, Owner, and AppraisedValue 626 627 type: ID (string), Color (string), Size (int), Owner (string), AppraisedValue (int). 628 629 The sample application then evaluates a query for 'asset13'. 630 631 Sample application ``'ReadAsset'`` call 632 633 .. code:: bash 634 635 console.log('\n--> Evaluate Transaction: ReadAsset, function returns an asset with a given assetID'); 636 result = await contract.evaluateTransaction('ReadAsset', 'asset13'); 637 console.log(`*** Result: ${prettyJSONString(result.toString())}`); 638 639 Chaincode ``'ReadAsset'`` function 640 641 .. code:: bash 642 643 // ReadAsset returns the asset stored in the world state with given id. 644 async ReadAsset(ctx, id) { 645 const assetJSON = await ctx.stub.getState(id); // get the asset from chaincode state 646 if (!assetJSON || assetJSON.length === 0) { 647 throw new Error(`The asset ${id} does not exist`); 648 } 649 return assetJSON.toString(); 650 } 651 652 Terminal output: 653 654 .. code:: bash 655 656 Evaluate Transaction: ReadAsset, function returns an asset with a given assetID 657 Result: { 658 "ID": "asset13", 659 "Color": "yellow", 660 "Size": "5", 661 "Owner": "Tom", 662 "AppraisedValue": "1300" 663 } 664 665 666 In the next part of the sequence, the sample application evaluates to see if 667 ``asset1`` exists, which will return a boolean value of true, because we populated 668 the ledger with ``asset1`` when we initialized the ledger with assets. You may recall 669 that the original appraised value of ``asset1`` was ``300``. 670 The application then submits a transaction to update ``asset1`` with a new appraised value, and 671 then immediately evaluates to read ``asset1`` from the ledger to show the new appraised value of ``350``. 672 673 Sample application ``'AssetExists'``, ``'UpdateAsset'``, and ``'ReadAsset'`` calls 674 675 .. code:: bash 676 677 console.log('\n--> Evaluate Transaction: AssetExists, function returns "true" if an asset with given assetID exist'); 678 result = await contract.evaluateTransaction('AssetExists', 'asset1'); 679 console.log(`*** Result: ${prettyJSONString(result.toString())}`); 680 681 console.log('\n--> Submit Transaction: UpdateAsset asset1, change the appraisedValue to 350'); 682 await contract.submitTransaction('UpdateAsset', 'asset1', 'blue', '5', 'Tomoko', '350'); 683 console.log('*** Result: committed'); 684 685 console.log('\n--> Evaluate Transaction: ReadAsset, function returns "asset1" attributes'); 686 result = await contract.evaluateTransaction('ReadAsset', 'asset1'); 687 console.log(`*** Result: ${prettyJSONString(result.toString())}`); 688 689 Chaincode ``'AssetExists'``, ``'UpdateAsset'``, and ``'ReadAsset'`` functions 690 691 .. code:: bash 692 693 // AssetExists returns true when asset with given ID exists in world state. 694 async AssetExists(ctx, id) { 695 const assetJSON = await ctx.stub.getState(id); 696 return assetJSON && assetJSON.length > 0; 697 } 698 // UpdateAsset updates an existing asset in the world state with provided parameters. 699 async UpdateAsset(ctx, id, color, size, owner, appraisedValue) { 700 const exists = await this.AssetExists(ctx, id); 701 if (!exists) { 702 throw new Error(`The asset ${id} does not exist`); 703 } 704 705 // overwriting original asset with new asset 706 const updatedAsset = { 707 ID: id, 708 Color: color, 709 Size: size, 710 Owner: owner, 711 AppraisedValue: appraisedValue, 712 }; 713 return ctx.stub.putState(id, Buffer.from(JSON.stringify(updatedAsset))); 714 } 715 // ReadAsset returns the asset stored in the world state with given id. 716 async ReadAsset(ctx, id) { 717 const assetJSON = await ctx.stub.getState(id); // get the asset from chaincode state 718 if (!assetJSON || assetJSON.length === 0) { 719 throw new Error(`The asset ${id} does not exist`); 720 } 721 return assetJSON.toString(); 722 } 723 724 Terminal Output: 725 726 .. code:: bash 727 728 Evaluate Transaction: AssetExists, function returns "true" if an asset with given assetID exist 729 Result: true 730 731 Submit Transaction: UpdateAsset asset1, change the appraisedValue to 350 732 733 Evaluate Transaction: ReadAsset, function returns "asset1" attributes 734 Result: { 735 "ID": "asset1", 736 "Color": "blue", 737 "Size": "5", 738 "Owner": "Tomoko", 739 "AppraisedValue": "350" 740 } 741 742 In this part of the sequence, the sample application attempts to submit 743 an ``'UdpateAsset'`` transaction for an asset that we know does not exist (``asset70``). 744 We expect that we will get an error because you cannot update an asset that does not exist, 745 which is why it is a good idea to check if an asset exists prior to attempting an 746 asset update or deletion. 747 748 Sample application ``'UpdateAsset'`` call 749 750 .. code:: bash 751 752 try { 753 // How about we try a transactions where the executing chaincode throws an error 754 // Notice how the submitTransaction will throw an error containing the error thrown by the chaincode 755 console.log('\n--> Submit Transaction: UpdateAsset asset70, asset70 does not exist and should return an error'); 756 await contract.submitTransaction('UpdateAsset', 'asset70', 'blue', '5', 'Tomoko', '300'); 757 console.log('******** FAILED to return an error'); 758 } catch (error) { 759 console.log(`*** Successfully caught the error: \n ${error}`); 760 } 761 762 Chaincode ``'UpdateAsset'`` function 763 764 .. code:: bash 765 766 // UpdateAsset updates an existing asset in the world state with provided parameters. 767 async UpdateAsset(ctx, id, color, size, owner, appraisedValue) { 768 const exists = await this.AssetExists(ctx, id); 769 if (!exists) { 770 throw new Error(`The asset ${id} does not exist`); 771 } 772 773 // overwriting original asset with new asset 774 const updatedAsset = { 775 ID: id, 776 Color: color, 777 Size: size, 778 Owner: owner, 779 AppraisedValue: appraisedValue, 780 }; 781 return ctx.stub.putState(id, Buffer.from(JSON.stringify(updatedAsset))); 782 } 783 784 Terminal output: 785 786 .. code:: bash 787 788 Submit Transaction: UpdateAsset asset70 789 2020-08-02T11:12:12.322Z - error: [Transaction]: Error: No valid responses from any peers. Errors: 790 peer=peer0.org1.example.com:7051, status=500, message=error in simulation: transaction returned with failure: Error: The asset asset70 does not exist 791 peer=peer0.org2.example.com:9051, status=500, message=error in simulation: transaction returned with failure: Error: The asset asset70 does not exist 792 Expected an error on UpdateAsset of non-existing Asset: Error: No valid responses from any peers. Errors: 793 peer=peer0.org1.example.com:7051, status=500, message=error in simulation: transaction returned with failure: Error: The asset asset70 does not exist 794 peer=peer0.org2.example.com:9051, status=500, message=error in simulation: transaction returned with failure: Error: The asset asset70 does not exist 795 796 797 In this final part of the sample application transaction sequence, the application 798 submits a transaction to transfer an existing asset to a new owner and then reads the 799 asset back from the ledger to display the new owner ``Tom``. 800 801 Sample application ``'TransferAsset'``, and ``'ReadAsset'`` calls 802 803 .. code:: bash 804 805 console.log('\n--> Submit Transaction: TransferAsset asset1, transfer to new owner of Tom'); 806 await contract.submitTransaction('TransferAsset', 'asset1', 'Tom'); 807 console.log('*** Result: committed'); 808 809 console.log('\n--> Evaluate Transaction: ReadAsset, function returns "asset1" attributes'); 810 result = await contract.evaluateTransaction('ReadAsset', 'asset1'); 811 console.log(`*** Result: ${prettyJSONString(result.toString())}`); 812 813 Chaincode ``'TransferAsset'``, and ``'ReadAsset'`` functions 814 815 .. code:: bash 816 817 // TransferAsset updates the owner field of asset with given id in the world state. 818 async TransferAsset(ctx, id, newOwner) { 819 const assetString = await this.ReadAsset(ctx, id); 820 const asset = JSON.parse(assetString); 821 asset.Owner = newOwner; 822 return ctx.stub.putState(id, Buffer.from(JSON.stringify(asset))); 823 } 824 // ReadAsset returns the asset stored in the world state with given id. 825 async ReadAsset(ctx, id) { 826 const assetJSON = await ctx.stub.getState(id); // get the asset from chaincode state 827 if (!assetJSON || assetJSON.length === 0) { 828 throw new Error(`The asset ${id} does not exist`); 829 } 830 return assetJSON.toString(); 831 } 832 833 Terminal output: 834 835 .. code:: bash 836 837 Submit Transaction: TransferAsset asset1, transfer to new owner of Tom 838 Evaluate Transaction: ReadAsset, function returns "asset1" attributes 839 Result: { 840 "ID": "asset1", 841 "Color": "blue", 842 "Size": "5", 843 "Owner": "Tom", 844 "AppraisedValue": "350" 845 } 846 847 848 A closer look 849 ------------- 850 851 Let's take a closer look at how the sample javascript application uses the APIs provided by the 852 `Fabric Node SDK <https://hyperledger.github.io/fabric-sdk-node/>`__ to 853 interact with our Fabric network. Use an editor (e.g. atom or visual studio) to 854 open ``app.js`` located in the ``asset-transfer-basic/application-javascript`` directory. 855 856 The application starts by bringing in scope two key classes from the 857 ``fabric-network`` module; ``Wallets`` and ``Gateway``. These classes 858 will be used to locate the ``appUser`` identity in the wallet, and use it to 859 connect to the network: 860 861 .. code:: bash 862 863 const { Gateway, Wallets } = require('fabric-network'); 864 865 First, the program sets up the gateway connection with the userId stored in the wallet and 866 specifies discovery options. 867 868 .. code:: bash 869 870 // setup the gateway instance 871 // The user will now be able to create connections to the fabric network and be able to 872 // submit transactions and query. All transactions submitted by this gateway will be 873 // signed by this user using the credentials stored in the wallet. 874 await gateway.connect(ccp, { 875 wallet, 876 identity: userId, 877 discovery: {enabled: true, asLocalhost: true} // using asLocalhost as this gateway is using a fabric network deployed locally 878 }); 879 880 Note at the top of the sample application code we require external utility files to build the CAClient, 881 registerUser, enrollAdmin, buildCCP (common connection profile), and buildWallet. 882 These utility programs are located in ``AppUtil.js`` in the ``test-application/javascript`` directory. 883 884 In ``AppUtil.js``, ``ccpPath`` describes the path to the connection profile that our application will use 885 to connect to our network. The connection profile was loaded from inside the 886 ``fabric-samples/test-network`` directory and parsed as a JSON file: 887 888 .. code:: bash 889 890 const ccpPath = path.resolve(__dirname, '..', '..', 'test-network','organizations','peerOrganizations','org1.example.com', 'connection-org1.json'); 891 892 If you'd like to understand more about the structure of a connection profile, 893 and how it defines the network, check out 894 `the connection profile topic <./developapps/connectionprofile.html>`_. 895 896 A network can be divided into multiple channels, and the next important line of 897 code connects the application to a particular channel within the network, 898 ``mychannel``, where our smart contract was deployed. Note that we assigned constants 899 near the top of the sample application to account for the channel name and the contract name: 900 901 .. code:: bash 902 903 const channelName = 'mychannel'; 904 const chaincodeName = 'basic'; 905 906 .. code:: bash 907 908 const network = await gateway.getNetwork(channelName); 909 910 Within this channel, we can access the asset-transfer ('basic') smart contract to interact 911 with the ledger: 912 913 .. code:: bash 914 915 const contract = network.getContract(chaincodeName); 916 917 918 Within asset-transfer ('basic') there are many different **transactions**, and our application 919 initially uses the ``InitLedger`` transaction to populate the ledger world state with 920 data: 921 922 .. code:: bash 923 924 await contract.submitTransaction('InitLedger'); 925 926 The ``evaluateTransaction`` method represents one of the simplest interactions 927 with a smart contract in blockchain network. It simply picks a peer defined in 928 the connection profile and sends the request to it, where it is evaluated. The 929 smart contract queries the assets on the peer's copy of the ledger and returns 930 the result to the application. This interaction does not result in an update of 931 the ledger. 932 933 ``submitTransaction`` is much more sophisticated than ``evaluateTransaction``. 934 Rather than interacting with a single peer, the SDK will send the 935 ``submitTransaction`` proposal to every required organization's peer in the 936 blockchain network based on the chaincode's endorsement policy. 937 Each of these peers will execute the requested smart 938 contract using this proposal, to generate a transaction response which it endorses (signs) 939 and returns to the SDK. The SDK collects all the endorsed transaction responses 940 into a single transaction, which it then submits to the orderer. The orderer 941 collects and sequences transactions from various application clients into a block of 942 transactions. These blocks are distributed to every peer in the network, 943 where every transaction is validated and committed. Finally, the SDK is 944 notified via an event, allowing it to return control to the application. 945 946 .. note:: ``submitTransaction`` includes an event listener that checks to make 947 sure the transaction has been validated and committed to the ledger. 948 Applications should either utilize a commit listener, or 949 leverage an API like ``submitTransaction`` that does this for you. 950 Without doing this, your transaction may not have been successfully 951 ordered, validated, and committed to the ledger. 952 953 ``submitTransaction`` does all this for the application! The process by which 954 the application, smart contract, peers and ordering service work together to 955 keep the ledger consistent across the network is called consensus, and it is 956 explained in detail in this `section <./peers/peers.html>`_. 957 958 Updating the ledger 959 ------------------- 960 961 From an application perspective, updating the ledger is simple. An application 962 submits a transaction to the blockchain network, and when it has been validated 963 and committed, the application receives a notification that the transaction has been successful. 964 Behind the scenes, this involves the process of consensus whereby the different components 965 of the blockchain network work together to ensure that every proposed update to the ledger 966 is valid and performed in an agreed and consistent order. 967 968 .. image:: tutorial/write_first_app.diagram.2.png 969 970 The asset-transfer ('basic') smart contract 971 ------------------------------------------- 972 The smart contract sample is available in the following languages: 973 974 - `Golang <https://github.com/hyperledger/fabric-samples/blob/master/asset-transfer-basic/chaincode-go>`__ 975 - `Java <https://github.com/hyperledger/fabric-samples/blob/master/asset-transfer-basic/chaincode-java>`__ 976 - `JavaScript <https://github.com/hyperledger/fabric-samples/blob/master/asset-transfer-basic/chaincode-javascript>`__ 977 - `Typescript <https://github.com/hyperledger/fabric-samples/blob/master/asset-transfer-basic/chaincode-typescript>`__ 978 979 Clean up 980 -------- 981 982 When you are finished using the asset-transfer sample, you can bring down the test 983 network using ``network.sh`` script. 984 985 986 .. code:: bash 987 988 ./network.sh down 989 990 This command will bring down the CAs, peers, and ordering node of the network 991 that we created. Note that all of the data on the ledger will be lost. 992 If you want to go through the tutorial again, you will start from a clean initial state. 993 994 Summary 995 ------- 996 997 Now that we’ve seen how the sample application and chaincode are written and how they interact with each other, you should have a pretty 998 good sense of how applications interact with a blockchain network using a smart 999 contract to query or update the ledger. You’ve seen the basics of the roles 1000 smart contracts, APIs, and the SDK play in queries and updates and you should 1001 have a feel for how different kinds of applications could be used to perform 1002 other business tasks and operations. 1003 1004 Additional resources 1005 -------------------- 1006 1007 As we said in the introduction, we have a whole section on 1008 :doc:`developapps/developing_applications` that includes in-depth information on 1009 smart contracts, process and data design, a tutorial using a more in-depth 1010 Commercial Paper `tutorial <./tutorial/commercial_paper.html>`_ and a large 1011 amount of other material relating to the development of applications. 1012 1013 .. Licensed under Creative Commons Attribution 4.0 International License 1014 https://creativecommons.org/licenses/by/4.0/