mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-17 07:25:10 +00:00
Asset Transfer typescript app using fabric-gateway
Signed-off-by: sapthasurendran <saptha.surendran@ibm.com> Added Fabric gateway and removed ca,tslint dependencies Signed-off-by: sapthasurendran <saptha.surendran@ibm.com> eslint migration Signed-off-by: sapthasurendran <saptha.surendran@ibm.com> Added gitignore Signed-off-by: sapthasurendran <saptha.surendran@ibm.com> Transaction Flow Changes Signed-off-by: sapthasurendran <saptha.surendran@ibm.com> Removed serviceClient import,removed outer try catch from main Signed-off-by: sapthasurendran <saptha.surendran@ibm.com> updated fabric-gateway version Signed-off-by: sapthasurendran <saptha.surendran@ibm.com> transaction verification flow change Signed-off-by: sapthasurendran <saptha.surendran@ibm.com> Improved main function readability Signed-off-by: sapthasurendran <saptha.surendran@ibm.com> extra comments Signed-off-by: sapthasurendran <saptha.surendran@ibm.com> Updated fabric-gateway to latest Signed-off-by: sapthasurendran <saptha.surendran@ibm.com>
This commit is contained in:
parent
576b2e74c9
commit
5355fb1b39
6 changed files with 387 additions and 0 deletions
|
|
@ -0,0 +1,45 @@
|
|||
{
|
||||
"env": {
|
||||
"node": true,
|
||||
"es6": true
|
||||
},
|
||||
"root": true,
|
||||
"ignorePatterns": [
|
||||
"dist/"
|
||||
],
|
||||
"extends": [
|
||||
"eslint:recommended"
|
||||
],
|
||||
"rules": {
|
||||
"indent": [
|
||||
"error",
|
||||
4
|
||||
],
|
||||
"quotes": [
|
||||
"error",
|
||||
"single"
|
||||
]
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"**/*.ts"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"impliedStrict": true
|
||||
}
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
14
asset-transfer-basic/application-gateway-typescript/.gitignore
vendored
Normal file
14
asset-transfer-basic/application-gateway-typescript/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Compiled TypeScript files
|
||||
dist
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
{
|
||||
"name": "asset-transfer-basic",
|
||||
"version": "1.0.0",
|
||||
"description": "Asset Transfer Basic Application implemented in typeScript using fabric-gateway",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
"engines": {
|
||||
"node": ">=12",
|
||||
"npm": ">=5"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint . --ext .ts",
|
||||
"pretest": "npm run lint",
|
||||
"start": "npm run build && node dist/app.js",
|
||||
"build": "tsc",
|
||||
"build:watch": "tsc -w",
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
"engineStrict": true,
|
||||
"author": "Hyperledger",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"fabric-gateway": "0.1.0-dev.20211006.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tsconfig/node12": "^1.0.9",
|
||||
"@typescript-eslint/eslint-plugin": "^4.31.2",
|
||||
"@typescript-eslint/parser": "^4.31.2",
|
||||
"eslint": "^7.32.0",
|
||||
"typescript": "^3.1.6"
|
||||
},
|
||||
"nyc": {
|
||||
"extension": [
|
||||
".ts",
|
||||
".tsx"
|
||||
],
|
||||
"exclude": [
|
||||
"coverage/**",
|
||||
"dist/**"
|
||||
],
|
||||
"reporter": [
|
||||
"text-summary",
|
||||
"html"
|
||||
],
|
||||
"all": true,
|
||||
"check-coverage": true,
|
||||
"statements": 100,
|
||||
"branches": 100,
|
||||
"functions": 100,
|
||||
"lines": 100
|
||||
}
|
||||
}
|
||||
246
asset-transfer-basic/application-gateway-typescript/src/app.ts
Normal file
246
asset-transfer-basic/application-gateway-typescript/src/app.ts
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
/*
|
||||
* Copyright IBM Corp. All Rights Reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import * as grpc from '@grpc/grpc-js';
|
||||
import * as crypto from 'crypto';
|
||||
import { connect, Identity, Signer, signers ,Contract} from 'fabric-gateway';
|
||||
import { promises as fs } from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
const channelName = 'mychannel';
|
||||
const chaincodeName = 'basic';
|
||||
const mspId = 'Org1MSP';
|
||||
|
||||
// path to crypto materials
|
||||
const cryptoPath = path.resolve(
|
||||
__dirname,
|
||||
'..',
|
||||
'..',
|
||||
'..',
|
||||
'test-network',
|
||||
'organizations',
|
||||
'peerOrganizations',
|
||||
'org1.example.com',
|
||||
);
|
||||
|
||||
//path to user private key directory
|
||||
const keyDirectoryPath = path.resolve(
|
||||
cryptoPath,
|
||||
'users',
|
||||
'User1@org1.example.com',
|
||||
'msp',
|
||||
'keystore'
|
||||
);
|
||||
//path to user certificate
|
||||
const certPath = path.resolve(
|
||||
cryptoPath,
|
||||
'users',
|
||||
'User1@org1.example.com',
|
||||
'msp',
|
||||
'signcerts',
|
||||
'cert.pem',
|
||||
);
|
||||
//path to peer tls certificate
|
||||
const tlsCertPath = path.resolve(
|
||||
cryptoPath,
|
||||
'peers',
|
||||
'peer0.org1.example.com',
|
||||
'tls',
|
||||
'ca.crt',
|
||||
);
|
||||
|
||||
//Gateway peer endpoint
|
||||
const peerEndpoint = 'localhost:7051';
|
||||
|
||||
// pre-requisites:
|
||||
// - fabric-sample two organization test-network setup with two peers and ordering service and and 2 certificate authorities
|
||||
// ===> from directory /fabric-samples/test-network
|
||||
// ./network.sh up createChannel
|
||||
// - Use any of the asset-transfer-basic chaincodes deployed on the channel "mychannel"
|
||||
// with the chaincode name of "basic". The following 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 -ccp ../asset-transfer-basic/chaincode-typescript/ -ccl typescript
|
||||
// - Be sure that node.js is installed
|
||||
// ===> from directory /fabric-samples/asset-transfer-basic/application-typescript
|
||||
// node -v
|
||||
// - npm installed code dependencies
|
||||
// ===> from directory /fabric-samples/asset-transfer-basic/application-typescript
|
||||
// npm install
|
||||
// - to run this test application
|
||||
// ===> from directory /fabric-samples/asset-transfer-basic/application-typescript
|
||||
// npm start
|
||||
|
||||
/**
|
||||
* A test application to show basic queries 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():Promise<void> {
|
||||
|
||||
// The gRPC client connection should be shared by all Gateway connections to this endpoint
|
||||
const client = await newGrpcConnection();
|
||||
|
||||
const gateway = connect({
|
||||
client,
|
||||
identity: await newIdentity(),
|
||||
signer: await newSigner(),
|
||||
});
|
||||
|
||||
try {
|
||||
|
||||
// Build a network instance based on the channel where the smart contract is deployed
|
||||
const network = 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.
|
||||
await initLedger(contract);
|
||||
|
||||
//Return all the current assets on the ledger.
|
||||
await getAllAssets(contract);
|
||||
|
||||
//Create new asset on the ledger.
|
||||
await createAsset(contract);
|
||||
|
||||
//Update an existing asset asynchronously.
|
||||
await updateAssetAsync(contract);
|
||||
|
||||
//Get the asset details by assetID
|
||||
await readAssetByID(contract);
|
||||
|
||||
//Update an asset which does not exist.
|
||||
await updateNonExistentAsset(contract)
|
||||
|
||||
} finally {
|
||||
gateway.close();
|
||||
client.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
main().catch(error => console.error('******** FAILED to run the application:', error));
|
||||
|
||||
async function newGrpcConnection(): Promise<grpc.Client> {
|
||||
const tlsRootCert = await fs.readFile(tlsCertPath);
|
||||
const tlsCredentials = grpc.credentials.createSsl(tlsRootCert);
|
||||
const GrpcClient = grpc.makeGenericClientConstructor({}, '');
|
||||
return new GrpcClient(peerEndpoint, tlsCredentials, {
|
||||
'grpc.ssl_target_name_override': 'peer0.org1.example.com',
|
||||
});
|
||||
}
|
||||
|
||||
async function newIdentity(): Promise<Identity> {
|
||||
const credentials = await fs.readFile(certPath);
|
||||
return { mspId, credentials };
|
||||
}
|
||||
|
||||
async function newSigner(): Promise<Signer> {
|
||||
const files = await fs.readdir(keyDirectoryPath);
|
||||
const privateKeyPem = await fs.readFile(path.resolve(keyDirectoryPath,files[0]));
|
||||
const privateKey = crypto.createPrivateKey(privateKeyPem);
|
||||
return signers.newPrivateKeySigner(privateKey);
|
||||
}
|
||||
|
||||
|
||||
async function initLedger(contract:Contract):Promise<void> {
|
||||
// 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',
|
||||
);
|
||||
|
||||
// Submit a transaction, blocking until the transaction has been committed on the ledger
|
||||
await contract.submitTransaction('InitLedger');
|
||||
|
||||
console.log('*** Result: committed');
|
||||
|
||||
}
|
||||
|
||||
async function getAllAssets(contract:Contract):Promise<void> {
|
||||
console.log(
|
||||
'\n--> Evaluate Transaction: GetAllAssets, function returns all the current assets on the ledger',
|
||||
);
|
||||
const result = await contract.evaluateTransaction('GetAllAssets');
|
||||
console.log(`*** Result: ${Buffer.from(result).toString()}`);
|
||||
}
|
||||
|
||||
async function createAsset(contract:Contract):Promise<void> {
|
||||
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');
|
||||
}
|
||||
|
||||
async function updateAssetAsync(contract:Contract):Promise<void> {
|
||||
|
||||
// Submit transaction asynchronously, blocking until the transaction has been sent to the orderer, and allowing
|
||||
// this thread to process the chaincode response (e.g. update a UI) without waiting for the commit notification
|
||||
|
||||
const commit = await contract.submitAsync('UpdateAsset', {
|
||||
arguments: ['asset1', 'blue', '5', 'Tomoko', '400'],
|
||||
});
|
||||
|
||||
console.log('*** Waiting for transaction commit');
|
||||
|
||||
|
||||
const status = await commit.getStatus();
|
||||
if (!status.successful) {
|
||||
throw new Error(`Transaction ${status.transactionId} failed to commit with status code ${status.code}`);
|
||||
}
|
||||
|
||||
console.log('*** Transaction committed successfully');
|
||||
|
||||
}
|
||||
|
||||
async function readAssetByID(contract:Contract):Promise<void> {
|
||||
console.log(
|
||||
'\n--> Evaluate Transaction: ReadAsset, function returns "asset1" attributes',
|
||||
);
|
||||
const result = await contract.evaluateTransaction('ReadAsset', 'asset1');
|
||||
console.log(`*** Result: ${Buffer.from(result).toString()}`);
|
||||
|
||||
}
|
||||
|
||||
async function updateNonExistentAsset(contract:Contract):Promise<void>{
|
||||
try {
|
||||
// How about we try a transaction 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}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"extends":"@tsconfig/node12/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"outDir": "dist",
|
||||
"moduleResolution": "node",
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"noImplicitAny": true
|
||||
},
|
||||
"include": [
|
||||
"./src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"./src/**/*.spec.ts"
|
||||
]
|
||||
}
|
||||
|
|
@ -63,6 +63,18 @@ node dist/app.js
|
|||
popd
|
||||
stopNetwork
|
||||
|
||||
# Run gateway typescript application
|
||||
createNetwork
|
||||
print "Initializing Typescript gateway application"
|
||||
pushd ../asset-transfer-basic/application-gateway-typescript
|
||||
npm install
|
||||
print "Building app.ts"
|
||||
npm run build
|
||||
print "Running the output app"
|
||||
node dist/app.js
|
||||
popd
|
||||
stopNetwork
|
||||
|
||||
# Run typescript HSM application
|
||||
createNetwork
|
||||
print "Initializing Typescript HSM application"
|
||||
|
|
|
|||
Loading…
Reference in a new issue