mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-17 15:35:09 +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
|
popd
|
||||||
stopNetwork
|
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
|
# Run typescript HSM application
|
||||||
createNetwork
|
createNetwork
|
||||||
print "Initializing Typescript HSM application"
|
print "Initializing Typescript HSM application"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue