/* * Copyright IBM Corp. All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ 'use strict'; const {Gateway, Wallets} = require('fabric-network'); const FabricCAServices = require('fabric-ca-client'); const path = require('path'); const {buildCAClient, registerUser, enrollAdmin} = require('../../test-application/javascript/CAUtil.js'); const {buildCCP, buildWallet} = require('../../test-application/javascript/AppUtil.js'); const channelName = 'mychannel'; const chaincodeName = 'basic'; const walletPath = path.join(__dirname, 'wallet'); const userId = 'appUser'; function prettyJSONString(inputString) { return JSON.stringify(JSON.parse(inputString), null, 2); } // pre-requisites: // - fabric-sample two organization test-network setup with two peers, ordering service, and 2 certificate authorities // ===> from directory /fabric-samples/test-network // network.sh run createChannel -ca // - any of the asset-transfer-basic chaincodes deployed on the channel "mychannel" with the chaincodeName of "basic" // This deploy command will package, install, approve, and commit the javascript chaincode, all the actions it takes // to deploy a chaincode to a channel. // ===> from directory /fabric-samples/test-network // network.sh deployCC -ccn basic -ccl javascript // - node install // - npm installed code dependencies // ===> from directory /fabric-samples/asset-transfer-basic/application-javascript // npm install // - to run this test application // ===> from directory /fabric-samples/asset-transfer-basic/application-javascript // node app.js // # this may be run again again /** * A test application to show basic operations with any of the asset-transfer-basic chaincodes * -- How to submit a transaction * -- How to query and check the results * * To see the SDK workings, try setting the logging to show on the console before running * export HFC_LOGGING='{"debug":"console"}' */ async function main() { try { // build an in memory object with the network configuration (also known as a connection profile) const ccp = buildCCP(); // build an instance of the fabric ca services client based on // the information in the network configuration const caClient = buildCAClient(FabricCAServices, ccp); // setup the wallet to hold the credentials of the application user const wallet = await buildWallet(Wallets, walletPath); // in a real application this would be done on an administrative flow, and only once await enrollAdmin(caClient, wallet); // in a real application this would be done only when a new user was required to be added // and would be part of an administrative flow await registerUser(caClient, wallet, userId, 'org1.department1'); // Create a new gateway instance for interacting with the fabric network. // In a real application this would be done as the backend server session is setup for // a user that has been verified. const gateway = new Gateway(); try { // setup the gateway instance // The user will now be able to create connections to the fabric network and be able to // submit transactions and query. All transactions submitted by this gateway will be // signed by this user using the credentials stored in the wallet. await gateway.connect(ccp, { wallet, identity: userId, discovery: {enabled: true, asLocalhost: true} // using asLocalhost as this gateway is using a fabric network deployed locally }); // Build a network instance based on the channel where the smart contract is deployed const network = await gateway.getNetwork(channelName); // Get the contract from the network. const contract = network.getContract(chaincodeName); // Initialize a set of asset data on the channel using the chaincode 'InitLedger' function. // This type of transaction would only be run once by an application the first time it was started after it // deployed the first time. Any updates to the chaincode deployed later would likely not need to run // an "init" type function. console.log('\n--> Submit Transaction: InitLedger, function creates the initial set of assets on the ledger'); await contract.submitTransaction('InitLedger'); console.log('*** Result: committed'); // Let's try a query type operation (function). // This will be sent to just one peer and the results will be shown. console.log('\n--> Evaluate Transaction: GetAllAssets, function returns all the current assets on the ledger'); let result = await contract.evaluateTransaction('GetAllAssets'); console.log(`*** Result: ${prettyJSONString(result.toString())}`); // Now let's try to submit a transaction. // This will be sent to both peers and if both peers endorse the transaction, the endorsed proposal will be sent // to the orderer to be committed by each of the peer's to the channel ledger. console.log('\n--> Submit Transaction: CreateAsset, creates new asset with ID, color, owner, size, and appraisedValue arguments'); await contract.submitTransaction('CreateAsset', 'asset13', 'yellow', '5', 'Tom', '1300'); console.log('*** Result: committed'); console.log('\n--> Evaluate Transaction: ReadAsset, function returns an asset with a given assetID'); result = await contract.evaluateTransaction('ReadAsset', 'asset13'); console.log(`*** Result: ${prettyJSONString(result.toString())}`); console.log('\n--> Evaluate Transaction: AssetExists, function returns "true" if an asset with given assetID exist'); result = await contract.evaluateTransaction('AssetExists', 'asset1'); console.log(`*** Result: ${prettyJSONString(result.toString())}`); console.log('\n--> Submit Transaction: UpdateAsset asset1, change the appraisedValue to 350'); await contract.submitTransaction('UpdateAsset', 'asset1', 'blue', '5', 'Tomoko', '350'); console.log('*** Result: committed'); console.log('\n--> Evaluate Transaction: ReadAsset, function returns "asset1" attributes'); result = await contract.evaluateTransaction('ReadAsset', 'asset1'); console.log(`*** Result: ${prettyJSONString(result.toString())}`); try { // How about we try a transactions where the executing chaincode throws an error // Notice how the submitTransaction will throw an error containing the error thrown by the chaincode console.log('\n--> Submit Transaction: UpdateAsset asset70, asset70 does not exist and should return an error'); await contract.submitTransaction('UpdateAsset', 'asset70', 'blue', '5', 'Tomoko', '300'); console.log('******** FAILED to return an error'); } catch (error) { console.log(`*** Successfully caught the error: \n ${error}`); } console.log('\n--> Submit Transaction: TransferAsset asset1, transfer to new owner of Tom'); await contract.submitTransaction('TransferAsset', 'asset1', 'Tom'); console.log('*** Result: committed'); console.log('\n--> Evaluate Transaction: ReadAsset, function returns "asset1" attributes'); result = await contract.evaluateTransaction('ReadAsset', 'asset1'); console.log(`*** Result: ${prettyJSONString(result.toString())}`); } finally { // Disconnect from the gateway when the application is closing // This will close all connections to the network gateway.disconnect(); } } catch (error) { console.error(`******** FAILED to run the application: ${error}`); } } main();