From 7771b4f2161074f23dbdb2d4076a141791dd757c Mon Sep 17 00:00:00 2001 From: Arnaud J Le Hors Date: Thu, 23 Jul 2020 17:06:57 +0200 Subject: [PATCH] Revert "Remove Typescript References (#258)" (#261) This reverts commit 7a23d7872a700352b99161fb0edd60e59cc866d7. --- README.md | 2 +- .../application-typescript/.gitignore | 17 ++ .../application-typescript/package.json | 59 +++++++ .../application-typescript/src/enrollAdmin.ts | 56 +++++++ .../application-typescript/src/invoke.ts | 59 +++++++ .../application-typescript/src/query.ts | 57 +++++++ .../src/registerUser.ts | 68 ++++++++ .../application-typescript/tsconfig.json | 16 ++ .../application-typescript/tslint.json | 22 +++ .../chaincode-typescript/.gitignore | 16 ++ .../chaincode-typescript/package.json | 62 +++++++ .../chaincode-typescript/src/asset.ts | 12 ++ .../chaincode-typescript/src/assetTransfer.ts | 153 ++++++++++++++++++ .../chaincode-typescript/src/index.ts | 8 + .../chaincode-typescript/tsconfig.json | 16 ++ .../chaincode-typescript/tslint.json | 21 +++ chaincode/README.md | 2 +- chaincode/abstore/javascript/.gitignore | 3 + chaincode/fabcar/javascript/.gitignore | 3 + chaincode/fabcar/typescript/.editorconfig | 16 ++ chaincode/fabcar/typescript/.gitignore | 81 ++++++++++ chaincode/fabcar/typescript/package.json | 62 +++++++ chaincode/fabcar/typescript/src/car.ts | 11 ++ chaincode/fabcar/typescript/src/fabcar.ts | 140 ++++++++++++++++ chaincode/fabcar/typescript/src/index.ts | 8 + chaincode/fabcar/typescript/tsconfig.json | 16 ++ chaincode/fabcar/typescript/tslint.json | 21 +++ chaincode/marbles02/javascript/.gitignore | 3 + ci/azure-pipelines.yml | 8 + .../fabcar/azure-pipelines-typescript.yml | 22 +++ ci/templates/test-network/azure-pipelines.yml | 1 + .../organization/digibank/contract/.npmignore | 3 + .../magnetocorp/contract/.npmignore | 3 + fabcar/javascript/.gitignore | 3 + fabcar/networkDown.sh | 1 + fabcar/startFabric.sh | 30 +++- fabcar/typescript/.editorconfig | 16 ++ fabcar/typescript/.gitignore | 83 ++++++++++ fabcar/typescript/package.json | 59 +++++++ fabcar/typescript/src/enrollAdmin.ts | 52 ++++++ fabcar/typescript/src/invoke.ts | 53 ++++++ fabcar/typescript/src/query.ts | 51 ++++++ fabcar/typescript/src/registerUser.ts | 64 ++++++++ fabcar/typescript/tsconfig.json | 16 ++ fabcar/typescript/tslint.json | 22 +++ fabcar/typescript/wallet/.gitkeep | 0 test-network/network.sh | 2 +- test-network/scripts/deployCC.sh | 14 +- 48 files changed, 1508 insertions(+), 5 deletions(-) create mode 100644 asset-transfer-basic/application-typescript/.gitignore create mode 100644 asset-transfer-basic/application-typescript/package.json create mode 100644 asset-transfer-basic/application-typescript/src/enrollAdmin.ts create mode 100644 asset-transfer-basic/application-typescript/src/invoke.ts create mode 100644 asset-transfer-basic/application-typescript/src/query.ts create mode 100644 asset-transfer-basic/application-typescript/src/registerUser.ts create mode 100644 asset-transfer-basic/application-typescript/tsconfig.json create mode 100644 asset-transfer-basic/application-typescript/tslint.json create mode 100644 asset-transfer-basic/chaincode-typescript/.gitignore create mode 100644 asset-transfer-basic/chaincode-typescript/package.json create mode 100644 asset-transfer-basic/chaincode-typescript/src/asset.ts create mode 100644 asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts create mode 100644 asset-transfer-basic/chaincode-typescript/src/index.ts create mode 100644 asset-transfer-basic/chaincode-typescript/tsconfig.json create mode 100644 asset-transfer-basic/chaincode-typescript/tslint.json create mode 100755 chaincode/fabcar/typescript/.editorconfig create mode 100644 chaincode/fabcar/typescript/.gitignore create mode 100644 chaincode/fabcar/typescript/package.json create mode 100644 chaincode/fabcar/typescript/src/car.ts create mode 100644 chaincode/fabcar/typescript/src/fabcar.ts create mode 100644 chaincode/fabcar/typescript/src/index.ts create mode 100644 chaincode/fabcar/typescript/tsconfig.json create mode 100644 chaincode/fabcar/typescript/tslint.json create mode 100644 ci/templates/fabcar/azure-pipelines-typescript.yml create mode 100755 fabcar/typescript/.editorconfig create mode 100644 fabcar/typescript/.gitignore create mode 100644 fabcar/typescript/package.json create mode 100644 fabcar/typescript/src/enrollAdmin.ts create mode 100644 fabcar/typescript/src/invoke.ts create mode 100644 fabcar/typescript/src/query.ts create mode 100644 fabcar/typescript/src/registerUser.ts create mode 100644 fabcar/typescript/tsconfig.json create mode 100644 fabcar/typescript/tslint.json create mode 100644 fabcar/typescript/wallet/.gitkeep diff --git a/README.md b/README.md index c86caa91..9c0877f9 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ The asset transfer series provides a series of smart contracts and applications | **Smart Contract** | **Description** | **Tutorial** | **Smart contract languages** | **Application languages** | | -----------|------------------------------|----------|---------|---------| -| [Basic](asset-transfer-basic) | The Basic sample smart contract that allows you to create and transfer an asset by putting data on the ledger and retrieving it. This sample is recommended for new Fabric users. | **Coming soon** | Go, JavaScript | JavaScript | +| [Basic](asset-transfer-basic) | The Basic sample smart contract that allows you to create and transfer an asset by putting data on the ledger and retrieving it. This sample is recommended for new Fabric users. | **Coming soon** | Go, JavaScript, Typescript | JavaScript, Typescript | | [Ledger queries](asset-transfer-ledger-queries) | The ledger queries sample demonstrates how to deploy an index with your chaincode and issue rich queries when you are using CouchDB as your state database. | **Coming soon** | Go | **Coming soon** | | [Private data](asset-transfer-private-data) | This sample demonstrates the use of private data collections and how the private data hash can be used to verify an agreement before executing a transfer | **Coming soon** | Go | **Coming soon** | | [Secured agreement](asset-transfer-secured-agreement) | Smart contract that uses private data, state based endorsement, and access control to establish the ownership of an asset, guarantee immutability, and securely transfer an asset with the consent of both the buyer and the owner, while keeping the asset details private. | [Secured asset transfer in Fabric](https://hyperledger-fabric.readthedocs.io/en/master/secured_private_asset_transfer_tutorial.html) | Go | **Coming soon** | diff --git a/asset-transfer-basic/application-typescript/.gitignore b/asset-transfer-basic/application-typescript/.gitignore new file mode 100644 index 00000000..e610e389 --- /dev/null +++ b/asset-transfer-basic/application-typescript/.gitignore @@ -0,0 +1,17 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# + +# Coverage directory used by tools like istanbul +coverage + +# Dependency directories +node_modules/ +jspm_packages/ +package-lock.json + +# Compiled TypeScript files +dist + +wallet +!wallet/.gitkeep diff --git a/asset-transfer-basic/application-typescript/package.json b/asset-transfer-basic/application-typescript/package.json new file mode 100644 index 00000000..03546d28 --- /dev/null +++ b/asset-transfer-basic/application-typescript/package.json @@ -0,0 +1,59 @@ +{ + "name": "asset-transfer-basic", + "version": "1.0.0", + "description": "Asset-Transfer-Basic application implemented in TypeScript", + "engines": { + "node": ">=8", + "npm": ">=5" + }, + "scripts": { + "lint": "tslint -c tslint.json 'src/**/*.ts'", + "pretest": "npm run lint", + "test": "nyc mocha -r ts-node/register src/**/*.spec.ts", + "build": "tsc", + "build:watch": "tsc -w", + "prepublishOnly": "npm run build" + }, + "engineStrict": true, + "author": "Hyperledger", + "license": "Apache-2.0", + "dependencies": { + "fabric-ca-client": "^2.1.0", + "fabric-network": "^2.1.0" + }, + "devDependencies": { + "@types/chai": "^4.1.7", + "@types/mocha": "^5.2.5", + "@types/node": "^10.12.10", + "@types/sinon": "^5.0.7", + "@types/sinon-chai": "^3.2.1", + "chai": "^4.2.0", + "mocha": "^5.2.0", + "nyc": "^14.1.1", + "sinon": "^7.1.1", + "sinon-chai": "^3.3.0", + "ts-node": "^7.0.1", + "tslint": "^5.11.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 + } +} diff --git a/asset-transfer-basic/application-typescript/src/enrollAdmin.ts b/asset-transfer-basic/application-typescript/src/enrollAdmin.ts new file mode 100644 index 00000000..43076c06 --- /dev/null +++ b/asset-transfer-basic/application-typescript/src/enrollAdmin.ts @@ -0,0 +1,56 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as FabricCAServices from 'fabric-ca-client'; +import { Wallets, X509Identity } from 'fabric-network'; +import * as fs from 'fs'; +import * as path from 'path'; + +async function main() { + try { + // load the network configuration + const ccpPath = path.resolve(__dirname, '..', '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json'); + const fileExists = fs.existsSync(ccpPath); + if (!fileExists) { + throw new Error(`no such file or directory: ${ccpPath}`); + } + const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); + + // Create a new CA client for interacting with the CA. + const caInfo = ccp.certificateAuthorities['ca.org1.example.com']; + const caTLSCACerts = caInfo.tlsCACerts.pem; + const ca = new FabricCAServices(caInfo.url, { trustedRoots: caTLSCACerts, verify: false }, caInfo.caName); + + // Create a new file system based wallet for managing identities. + const walletPath = path.join(path.resolve(__dirname, '..'), 'wallet'); + const wallet = await Wallets.newFileSystemWallet(walletPath); + console.log(`Wallet path: ${walletPath}`); + + // Check to see if we've already enrolled the admin user. + const identity = await wallet.get('admin'); + if (identity) { + console.log('An identity for the admin user "admin" already exists in the wallet'); + return; + } + + // Enroll the admin user, and import the new identity into the wallet. + const enrollment = await ca.enroll({ enrollmentID: 'admin', enrollmentSecret: 'adminpw' }); + const x509Identity: X509Identity = { + credentials: { + certificate: enrollment.certificate, + privateKey: enrollment.key.toBytes(), + }, + mspId: 'Org1MSP', + type: 'X.509', + }; + await wallet.put('admin', x509Identity); + console.log('Successfully enrolled admin user "admin" and imported it into the wallet'); + + } catch (error) { + console.error(`Failed to enroll admin user "admin": ${error}`); + process.exit(1); + } +} + +main(); diff --git a/asset-transfer-basic/application-typescript/src/invoke.ts b/asset-transfer-basic/application-typescript/src/invoke.ts new file mode 100644 index 00000000..205f3212 --- /dev/null +++ b/asset-transfer-basic/application-typescript/src/invoke.ts @@ -0,0 +1,59 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Gateway, Wallets } from 'fabric-network'; +import * as path from 'path'; +import * as fs from 'fs'; + +async function main() { + try { + // load the network configuration + const ccpPath = path.resolve(__dirname, '..', '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json'); + const fileExists = fs.existsSync(ccpPath); + if (!fileExists) { + throw new Error(`no such file or directory: ${ccpPath}`); + } + const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); + + // Create a new file system based wallet for managing identities. + const walletPath = path.join(path.resolve(__dirname, '..'), 'wallet'); + const wallet = await Wallets.newFileSystemWallet(walletPath); + console.log(`Wallet path: ${walletPath}`); + + // Check to see if we've already enrolled the user. + const identity = await wallet.get('appUser'); + if (!identity) { + console.log('An identity for the user "appUser" does not exist in the wallet'); + console.log('Run the registerUser.ts application before retrying'); + return; + } + + // Create a new gateway for connecting to our peer node. + const gateway = new Gateway(); + await gateway.connect(ccp, { wallet, identity: 'appUser', discovery: { enabled: true, asLocalhost: true } }); + + // Get the network (channel) our contract is deployed to. + const network = await gateway.getNetwork('mychannel'); + + // Get the contract from the network. + const contract = network.getContract('basic'); + + // Submit the specified transaction. (several example transactions are listed below) + // createAsset creates an asset with ID asset1, color yellow, owner Dave, size 5 and appraizedValue of 130 requires 6 arguments. + // ex: ('createAsset', 'asset1', 'yellow', 'Dave', 5, 1300) + // transferAsset transfers an asset with ID asset1 to new owner Tom - requires 2 arguments. + // ex: ('transferAsset', 'asset1', 'Tom') + await contract.submitTransaction('createAsset', 'asset13', 'yellow', "5", 'Tom', "1300"); + console.log(`Transaction has been submitted`); + + // Disconnect from the gateway. + await gateway.disconnect(); + + } catch (error) { + console.error(`Failed to submit transaction: ${error}`); + process.exit(1); + } +} + +main(); diff --git a/asset-transfer-basic/application-typescript/src/query.ts b/asset-transfer-basic/application-typescript/src/query.ts new file mode 100644 index 00000000..aae2243f --- /dev/null +++ b/asset-transfer-basic/application-typescript/src/query.ts @@ -0,0 +1,57 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Gateway, Wallets } from 'fabric-network'; +import * as path from 'path'; +import * as fs from 'fs'; + + +async function main() { + try { + // load the network configuration + const ccpPath = path.resolve(__dirname, '..', '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json'); + const fileExists = fs.existsSync(ccpPath); + if (!fileExists) { + throw new Error(`no such file or directory: ${ccpPath}`); + } + const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); + + // Create a new file system based wallet for managing identities. + const walletPath = path.join(path.resolve(__dirname, '..'), 'wallet'); + const wallet = await Wallets.newFileSystemWallet(walletPath); + console.log(`Wallet path: ${walletPath}`); + + // Check to see if we've already enrolled the user. + const identity = await wallet.get('appUser'); + if (!identity) { + console.log('An identity for the user "appUser" does not exist in the wallet'); + console.log('Run the registerUser.ts application before retrying'); + return; + } + + // Create a new gateway for connecting to our peer node. + const gateway = new Gateway(); + await gateway.connect(ccp, { wallet, identity: 'appUser', discovery: { enabled: true, asLocalhost: true } }); + + // Get the network (channel) our contract is deployed to. + const network = await gateway.getNetwork('mychannel'); + + // Get the contract from the network. + const contract = network.getContract('basic'); + + // Evaluate the specified transaction. (several example transactions are listed below) + // getAsset returns an asset with given assetID asset4 - requires 1 argument. + // ex: ('getAsset', 'asset4') + // getAllAssets returns all assets in the world state - requires no arguments. + // ex: ('getAllAssets') + const result = await contract.evaluateTransaction('getAllAssets'); + console.log(`Transaction has been evaluated, result is: ${result.toString()}`); + + } catch (error) { + console.error(`Failed to evaluate transaction: ${error}`); + process.exit(1); + } +} + +main(); diff --git a/asset-transfer-basic/application-typescript/src/registerUser.ts b/asset-transfer-basic/application-typescript/src/registerUser.ts new file mode 100644 index 00000000..0b6b7a2c --- /dev/null +++ b/asset-transfer-basic/application-typescript/src/registerUser.ts @@ -0,0 +1,68 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Wallets, X509Identity } from 'fabric-network'; +import * as FabricCAServices from 'fabric-ca-client'; +import * as path from 'path'; +import * as fs from 'fs'; + +async function main() { + try { + // load the network configuration + const ccpPath = path.resolve(__dirname, '..', '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json'); + const fileExists = fs.existsSync(ccpPath); + if (!fileExists) { + throw new Error(`no such file or directory: ${ccpPath}`); + } + let ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); + + // Create a new CA client for interacting with the CA. + const caURL = ccp.certificateAuthorities['ca.org1.example.com'].url; + const ca = new FabricCAServices(caURL); + + // Create a new file system based wallet for managing identities. + const walletPath = path.join(path.resolve(__dirname, '..'), 'wallet'); + const wallet = await Wallets.newFileSystemWallet(walletPath); + console.log(`Wallet path: ${walletPath}`); + + // Check to see if we've already enrolled the user. + const userIdentity = await wallet.get('appUser'); + if (userIdentity) { + console.log('An identity for the user "appUser" already exists in the wallet'); + return; + } + + // Check to see if we've already enrolled the admin user. + const adminIdentity = await wallet.get('admin'); + if (!adminIdentity) { + console.log('An identity for the admin user "admin" does not exist in the wallet'); + console.log('Run the enrollAdmin.ts application before retrying'); + return; + } + + // build a user object for authenticating with the CA + const provider = wallet.getProviderRegistry().getProvider(adminIdentity.type); + const adminUser = await provider.getUserContext(adminIdentity, 'admin'); + + // Register the user, enroll the user, and import the new identity into the wallet. + const secret = await ca.register({ affiliation: 'org1.department1', enrollmentID: 'appUser', role: 'client' }, adminUser); + const enrollment = await ca.enroll({ enrollmentID: 'appUser', enrollmentSecret: secret }); + const x509Identity: X509Identity = { + credentials: { + certificate: enrollment.certificate, + privateKey: enrollment.key.toBytes(), + }, + mspId: 'Org1MSP', + type: 'X.509', + }; + await wallet.put('appUser', x509Identity); + console.log('Successfully registered and enrolled admin user "appUser" and imported it into the wallet'); + + } catch (error) { + console.error(`Failed to register user "appUser": ${error}`); + process.exit(1); + } +} + +main(); diff --git a/asset-transfer-basic/application-typescript/tsconfig.json b/asset-transfer-basic/application-typescript/tsconfig.json new file mode 100644 index 00000000..8c96ea07 --- /dev/null +++ b/asset-transfer-basic/application-typescript/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "outDir": "dist", + "target": "es2017", + "moduleResolution": "node", + "module": "commonjs", + "declaration": true, + "sourceMap": true + }, + "include": [ + "./src/**/*" + ], + "exclude": [ + "./src/**/*.spec.ts" + ] +} diff --git a/asset-transfer-basic/application-typescript/tslint.json b/asset-transfer-basic/application-typescript/tslint.json new file mode 100644 index 00000000..e08fd0b8 --- /dev/null +++ b/asset-transfer-basic/application-typescript/tslint.json @@ -0,0 +1,22 @@ +{ + "defaultSeverity": "error", + "extends": [ + "tslint:recommended" + ], + "jsRules": {}, + "rules": { + "indent": [true, "spaces", 4], + "linebreak-style": [true, "LF"], + "quotemark": [true, "single"], + "semicolon": [true, "always"], + "no-console": false, + "curly": true, + "triple-equals": true, + "no-string-throw": true, + "no-var-keyword": true, + "no-trailing-whitespace": true, + "object-literal-key-quotes": [true, "as-needed"], + "max-line-length": false + }, + "rulesDirectory": [] +} diff --git a/asset-transfer-basic/chaincode-typescript/.gitignore b/asset-transfer-basic/chaincode-typescript/.gitignore new file mode 100644 index 00000000..79bfe1a3 --- /dev/null +++ b/asset-transfer-basic/chaincode-typescript/.gitignore @@ -0,0 +1,16 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# + + +# Coverage directory used by tools like istanbul +coverage + +# Dependency directories +node_modules/ +jspm_packages/ +package-lock.json + +# Compiled TypeScript files +dist + diff --git a/asset-transfer-basic/chaincode-typescript/package.json b/asset-transfer-basic/chaincode-typescript/package.json new file mode 100644 index 00000000..2b681fc1 --- /dev/null +++ b/asset-transfer-basic/chaincode-typescript/package.json @@ -0,0 +1,62 @@ +{ + "name": "asset-transfer-basic", + "version": "1.0.0", + "description": "Asset Transfer Basic contract implemented in TypeScript", + "main": "dist/index.js", + "typings": "dist/index.d.ts", + "engines": { + "node": ">=12", + "npm": ">=5" + }, + "scripts": { + "lint": "tslint -c tslint.json 'src/**/*.ts'", + "pretest": "npm run lint", + "test": "nyc mocha -r ts-node/register src/**/*.spec.ts", + "start": "fabric-chaincode-node start", + "build": "tsc", + "build:watch": "tsc -w", + "prepublishOnly": "npm run build" + }, + "engineStrict": true, + "author": "Hyperledger", + "license": "Apache-2.0", + "dependencies": { + "fabric-contract-api": "^2.0.0", + "fabric-shim": "^2.0.0" + }, + "devDependencies": { + "@types/chai": "^4.1.7", + "@types/mocha": "^5.2.5", + "@types/node": "^10.12.10", + "@types/sinon": "^5.0.7", + "@types/sinon-chai": "^3.2.1", + "chai": "^4.2.0", + "mocha": "^5.2.0", + "nyc": "^14.1.1", + "sinon": "^7.1.1", + "sinon-chai": "^3.3.0", + "ts-node": "^7.0.1", + "tslint": "^5.11.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 + } +} diff --git a/asset-transfer-basic/chaincode-typescript/src/asset.ts b/asset-transfer-basic/chaincode-typescript/src/asset.ts new file mode 100644 index 00000000..56d33439 --- /dev/null +++ b/asset-transfer-basic/chaincode-typescript/src/asset.ts @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +export class Asset { + public docType?: string; + public ID: string; + public Color: string; + public Size: number; + public Owner: string; + public AppraisedValue: number; +} diff --git a/asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts b/asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts new file mode 100644 index 00000000..04ebcb99 --- /dev/null +++ b/asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts @@ -0,0 +1,153 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Context, Contract } from 'fabric-contract-api'; +import { Asset } from './asset'; + +export class AssetTransfer extends Contract { + + public async initLedger(ctx: Context) { + const assets: Asset[] = [ + { + ID: "asset1", + Color: "blue", + Size: 5, + Owner: "Tomoko", + AppraisedValue: 300, + }, + { + ID: "asset2", + Color: "red", + Size: 5, + Owner: "Brad", + AppraisedValue: 400, + }, + { + ID: "asset3", + Color: "green", + Size: 10, + Owner: "Jin Soo", + AppraisedValue: 500, + }, + { + ID: "asset4", + Color: "yellow", + Size: 10, + Owner: "Max", + AppraisedValue: 600, + }, + { + ID: "asset5", + Color: "black", + Size: 15, + Owner: "Adriana", + AppraisedValue: 700, + }, + { + ID: "asset6", + Color: "white", + Size: 15, + Owner: "Michel", + AppraisedValue: 800, + }, + ]; + + for (let i = 0; i < assets.length; i++) { + assets[i].docType = 'asset'; + await ctx.stub.putState(assets[i].ID, Buffer.from(JSON.stringify(assets[i]))); + console.info('Added <--> ', assets[i]); + } + } + + // createAsset issues a new asset to the world state with given details. + public async createAsset(ctx: Context, id: string, color: string, size: number, owner: string, appraisedValue: number) { + const asset = { + ID: id, + Color: color, + Size: size, + Owner: owner, + AppraisedValue: appraisedValue, + }; + + await ctx.stub.putState(id, Buffer.from(JSON.stringify(asset))); + } + + // readAsset returns the asset stored in the world state with given id. + public async readAsset(ctx: Context, id: string): Promise { + const assetJSON = await ctx.stub.getState(id); // get the asset from chaincode state + if (!assetJSON || assetJSON.length === 0) { + throw new Error(`The asset ${id} does not exist`); + } + + return assetJSON.toString(); + } + + // updateAsset updates an existing asset in the world state with provided parameters. + public async updateAsset(ctx: Context, id: string, color: string, size: number, owner: string, appraisedValue: number) { + const exists = await this.assetExists(ctx, id); + if (!exists) { + throw new Error(`The asset ${id} does not exist`); + } + + // overwritting original asset with new asset + let updatedAsset = { + ID: id, + Color: color, + Size: size, + Owner: owner, + AppraisedValue: appraisedValue, + }; + + return ctx.stub.putState(id, Buffer.from(JSON.stringify(updatedAsset))); + } + + // deleteAsset deletes an given asset from the world state. + public async deleteAsset(ctx: Context, id: string) { + const exists = await this.assetExists(ctx, id); + if (!exists) { + throw new Error(`The asset ${id} does not exist`); + } + + return ctx.stub.deleteState(id); + } + + // assetExists returns true when asset with given ID exists in world state. + public async assetExists(ctx: Context, id: string): Promise { + const assetJSON = await ctx.stub.getState(id); + if (!assetJSON || assetJSON.length === 0) { + return false; + } + return true; + } + + // transferAsset updates the owner field of asset with given id in the world state. + public async transferAsset(ctx: Context, id: string, newOwner: string) { + let assetString = await this.readAsset(ctx, id); + + let asset = JSON.parse(assetString); + asset.Owner = newOwner; + + await ctx.stub.putState(id, Buffer.from(JSON.stringify(asset))); + } + + // getAllAssets returns all assets found in the world state. + public async getAllAssets(ctx: Context): Promise { + const allResults = []; + // range query with empty string for startKey and endKey does an open-ended query of all assets in the chaincode namespace. + for await (const { key, value } of ctx.stub.getStateByRange("", "")) { + const strValue = Buffer.from(value).toString('utf8'); + let record; + try { + record = JSON.parse(strValue); + } catch (err) { + console.log(err); + record = strValue; + } + allResults.push({ Key: key, Record: record }); + } + console.info(allResults); + return JSON.stringify(allResults); + } + +} diff --git a/asset-transfer-basic/chaincode-typescript/src/index.ts b/asset-transfer-basic/chaincode-typescript/src/index.ts new file mode 100644 index 00000000..a5f5e17b --- /dev/null +++ b/asset-transfer-basic/chaincode-typescript/src/index.ts @@ -0,0 +1,8 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +import { AssetTransfer } from './assetTransfer'; +export { AssetTransfer } from './assetTransfer'; + +export const contracts: any[] = [AssetTransfer]; diff --git a/asset-transfer-basic/chaincode-typescript/tsconfig.json b/asset-transfer-basic/chaincode-typescript/tsconfig.json new file mode 100644 index 00000000..8c96ea07 --- /dev/null +++ b/asset-transfer-basic/chaincode-typescript/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "outDir": "dist", + "target": "es2017", + "moduleResolution": "node", + "module": "commonjs", + "declaration": true, + "sourceMap": true + }, + "include": [ + "./src/**/*" + ], + "exclude": [ + "./src/**/*.spec.ts" + ] +} diff --git a/asset-transfer-basic/chaincode-typescript/tslint.json b/asset-transfer-basic/chaincode-typescript/tslint.json new file mode 100644 index 00000000..33ccbf3c --- /dev/null +++ b/asset-transfer-basic/chaincode-typescript/tslint.json @@ -0,0 +1,21 @@ +{ + "defaultSeverity": "error", + "extends": [ + "tslint:recommended" + ], + "jsRules": {}, + "rules": { + "indent": [true, "spaces", 4], + "linebreak-style": [true, "LF"], + "quotemark": [true, "single"], + "semicolon": [true, "always"], + "no-console": false, + "curly": true, + "triple-equals": true, + "no-string-throw": true, + "no-var-keyword": true, + "no-trailing-whitespace": true, + "object-literal-key-quotes": [true, "as-needed"] + }, + "rulesDirectory": [] +} diff --git a/chaincode/README.md b/chaincode/README.md index 603de32c..ceeb0aab 100644 --- a/chaincode/README.md +++ b/chaincode/README.md @@ -6,7 +6,7 @@ This folder contains example smart contracts that are used by the Hyperledger Fa | **Smart Contract** | **Description** | **Tutorial** | **Languages** | | -----------|------------------------------|----------|---------| -| [fabcar](fabcar) | Basic smart contract that allows you to add and change data on the ledger using the Fabric contract API. Also contains an example on how to run chaincode as an external service. | [Writing your first application](https://hyperledger-fabric.readthedocs.io/en/master/write_first_app.html) | Go, Java, JavaScript | +| [fabcar](fabcar) | Basic smart contract that allows you to add and change data on the ledger using the Fabric contract API. Also contains an example on how to run chaincode as an external service. | [Writing your first application](https://hyperledger-fabric.readthedocs.io/en/master/write_first_app.html) | Go, Java, JavaScript, Typescript | | [marbles02](marbles02) | Sample that demonstrates how to deploy an index and use rich queries when you are using CouchDB as your state database. | [Using CouchDB](https://hyperledger-fabric.readthedocs.io/en/master/couchdb_tutorial.html) | Go | | [marbles02_private](marbles02_private) | Sample that demonstrates the use of private data collections. | [Private data tutorial](https://hyperledger-fabric.readthedocs.io/en/master/private_data_tutorial.html) | Go | | [marbles_transfer](marbles_transfer) | Smart contract that demonstrates the use of private data, state based endorsement, and access control to securely transfer an asset between two parties | [Marbles private asset transfer scenario](marbles_transfer/README.md) | Go | diff --git a/chaincode/abstore/javascript/.gitignore b/chaincode/abstore/javascript/.gitignore index 9f444c23..a00ca941 100644 --- a/chaincode/abstore/javascript/.gitignore +++ b/chaincode/abstore/javascript/.gitignore @@ -40,6 +40,9 @@ build/Release node_modules/ jspm_packages/ +# TypeScript v1 declaration files +typings/ + # Optional npm cache directory .npm diff --git a/chaincode/fabcar/javascript/.gitignore b/chaincode/fabcar/javascript/.gitignore index 9f444c23..a00ca941 100644 --- a/chaincode/fabcar/javascript/.gitignore +++ b/chaincode/fabcar/javascript/.gitignore @@ -40,6 +40,9 @@ build/Release node_modules/ jspm_packages/ +# TypeScript v1 declaration files +typings/ + # Optional npm cache directory .npm diff --git a/chaincode/fabcar/typescript/.editorconfig b/chaincode/fabcar/typescript/.editorconfig new file mode 100755 index 00000000..75a13be2 --- /dev/null +++ b/chaincode/fabcar/typescript/.editorconfig @@ -0,0 +1,16 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# + +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/chaincode/fabcar/typescript/.gitignore b/chaincode/fabcar/typescript/.gitignore new file mode 100644 index 00000000..69d6a33b --- /dev/null +++ b/chaincode/fabcar/typescript/.gitignore @@ -0,0 +1,81 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# next.js build output +.next + +# nuxt.js build output +.nuxt + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless + +# Compiled TypeScript files +dist + diff --git a/chaincode/fabcar/typescript/package.json b/chaincode/fabcar/typescript/package.json new file mode 100644 index 00000000..7cd3837c --- /dev/null +++ b/chaincode/fabcar/typescript/package.json @@ -0,0 +1,62 @@ +{ + "name": "fabcar", + "version": "1.0.0", + "description": "FabCar contract implemented in TypeScript", + "main": "dist/index.js", + "typings": "dist/index.d.ts", + "engines": { + "node": ">=8", + "npm": ">=5" + }, + "scripts": { + "lint": "tslint -c tslint.json 'src/**/*.ts'", + "pretest": "npm run lint", + "test": "nyc mocha -r ts-node/register src/**/*.spec.ts", + "start": "fabric-chaincode-node start", + "build": "tsc", + "build:watch": "tsc -w", + "prepublishOnly": "npm run build" + }, + "engineStrict": true, + "author": "Hyperledger", + "license": "Apache-2.0", + "dependencies": { + "fabric-contract-api": "^2.0.0", + "fabric-shim": "^2.0.0" + }, + "devDependencies": { + "@types/chai": "^4.1.7", + "@types/mocha": "^5.2.5", + "@types/node": "^10.12.10", + "@types/sinon": "^5.0.7", + "@types/sinon-chai": "^3.2.1", + "chai": "^4.2.0", + "mocha": "^5.2.0", + "nyc": "^14.1.1", + "sinon": "^7.1.1", + "sinon-chai": "^3.3.0", + "ts-node": "^7.0.1", + "tslint": "^5.11.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 + } +} diff --git a/chaincode/fabcar/typescript/src/car.ts b/chaincode/fabcar/typescript/src/car.ts new file mode 100644 index 00000000..ba101625 --- /dev/null +++ b/chaincode/fabcar/typescript/src/car.ts @@ -0,0 +1,11 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +export class Car { + public docType?: string; + public color: string; + public make: string; + public model: string; + public owner: string; +} diff --git a/chaincode/fabcar/typescript/src/fabcar.ts b/chaincode/fabcar/typescript/src/fabcar.ts new file mode 100644 index 00000000..597da7e2 --- /dev/null +++ b/chaincode/fabcar/typescript/src/fabcar.ts @@ -0,0 +1,140 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Context, Contract } from 'fabric-contract-api'; +import { Car } from './car'; + +export class FabCar extends Contract { + + public async initLedger(ctx: Context) { + console.info('============= START : Initialize Ledger ==========='); + const cars: Car[] = [ + { + color: 'blue', + make: 'Toyota', + model: 'Prius', + owner: 'Tomoko', + }, + { + color: 'red', + make: 'Ford', + model: 'Mustang', + owner: 'Brad', + }, + { + color: 'green', + make: 'Hyundai', + model: 'Tucson', + owner: 'Jin Soo', + }, + { + color: 'yellow', + make: 'Volkswagen', + model: 'Passat', + owner: 'Max', + }, + { + color: 'black', + make: 'Tesla', + model: 'S', + owner: 'Adriana', + }, + { + color: 'purple', + make: 'Peugeot', + model: '205', + owner: 'Michel', + }, + { + color: 'white', + make: 'Chery', + model: 'S22L', + owner: 'Aarav', + }, + { + color: 'violet', + make: 'Fiat', + model: 'Punto', + owner: 'Pari', + }, + { + color: 'indigo', + make: 'Tata', + model: 'Nano', + owner: 'Valeria', + }, + { + color: 'brown', + make: 'Holden', + model: 'Barina', + owner: 'Shotaro', + }, + ]; + + for (let i = 0; i < cars.length; i++) { + cars[i].docType = 'car'; + await ctx.stub.putState('CAR' + i, Buffer.from(JSON.stringify(cars[i]))); + console.info('Added <--> ', cars[i]); + } + console.info('============= END : Initialize Ledger ==========='); + } + + public async queryCar(ctx: Context, carNumber: string): Promise { + const carAsBytes = await ctx.stub.getState(carNumber); // get the car from chaincode state + if (!carAsBytes || carAsBytes.length === 0) { + throw new Error(`${carNumber} does not exist`); + } + console.log(carAsBytes.toString()); + return carAsBytes.toString(); + } + + public async createCar(ctx: Context, carNumber: string, make: string, model: string, color: string, owner: string) { + console.info('============= START : Create Car ==========='); + + const car: Car = { + color, + docType: 'car', + make, + model, + owner, + }; + + await ctx.stub.putState(carNumber, Buffer.from(JSON.stringify(car))); + console.info('============= END : Create Car ==========='); + } + + public async queryAllCars(ctx: Context): Promise { + const startKey = ''; + const endKey = ''; + const allResults = []; + for await (const {key, value} of ctx.stub.getStateByRange(startKey, endKey)) { + const strValue = Buffer.from(value).toString('utf8'); + let record; + try { + record = JSON.parse(strValue); + } catch (err) { + console.log(err); + record = strValue; + } + allResults.push({ Key: key, Record: record }); + } + console.info(allResults); + return JSON.stringify(allResults); + } + + public async changeCarOwner(ctx: Context, carNumber: string, newOwner: string) { + console.info('============= START : changeCarOwner ==========='); + + const carAsBytes = await ctx.stub.getState(carNumber); // get the car from chaincode state + if (!carAsBytes || carAsBytes.length === 0) { + throw new Error(`${carNumber} does not exist`); + } + const car: Car = JSON.parse(carAsBytes.toString()); + car.owner = newOwner; + + await ctx.stub.putState(carNumber, Buffer.from(JSON.stringify(car))); + console.info('============= END : changeCarOwner ==========='); + } + +} diff --git a/chaincode/fabcar/typescript/src/index.ts b/chaincode/fabcar/typescript/src/index.ts new file mode 100644 index 00000000..c0a2fcf6 --- /dev/null +++ b/chaincode/fabcar/typescript/src/index.ts @@ -0,0 +1,8 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +import { FabCar } from './fabcar'; +export { FabCar } from './fabcar'; + +export const contracts: any[] = [ FabCar ]; diff --git a/chaincode/fabcar/typescript/tsconfig.json b/chaincode/fabcar/typescript/tsconfig.json new file mode 100644 index 00000000..8c96ea07 --- /dev/null +++ b/chaincode/fabcar/typescript/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "outDir": "dist", + "target": "es2017", + "moduleResolution": "node", + "module": "commonjs", + "declaration": true, + "sourceMap": true + }, + "include": [ + "./src/**/*" + ], + "exclude": [ + "./src/**/*.spec.ts" + ] +} diff --git a/chaincode/fabcar/typescript/tslint.json b/chaincode/fabcar/typescript/tslint.json new file mode 100644 index 00000000..33ccbf3c --- /dev/null +++ b/chaincode/fabcar/typescript/tslint.json @@ -0,0 +1,21 @@ +{ + "defaultSeverity": "error", + "extends": [ + "tslint:recommended" + ], + "jsRules": {}, + "rules": { + "indent": [true, "spaces", 4], + "linebreak-style": [true, "LF"], + "quotemark": [true, "single"], + "semicolon": [true, "always"], + "no-console": false, + "curly": true, + "triple-equals": true, + "no-string-throw": true, + "no-var-keyword": true, + "no-trailing-whitespace": true, + "object-literal-key-quotes": [true, "as-needed"] + }, + "rulesDirectory": [] +} diff --git a/chaincode/marbles02/javascript/.gitignore b/chaincode/marbles02/javascript/.gitignore index 9f444c23..a00ca941 100644 --- a/chaincode/marbles02/javascript/.gitignore +++ b/chaincode/marbles02/javascript/.gitignore @@ -40,6 +40,9 @@ build/Release node_modules/ jspm_packages/ +# TypeScript v1 declaration files +typings/ + # Optional npm cache directory .npm diff --git a/ci/azure-pipelines.yml b/ci/azure-pipelines.yml index 93bcee56..dc336a0e 100644 --- a/ci/azure-pipelines.yml +++ b/ci/azure-pipelines.yml @@ -60,6 +60,14 @@ jobs: - template: templates/install-deps.yml - template: templates/fabcar/azure-pipelines-javascript.yml + - job: Fabcar_TypeScript + displayName: FabCar (TypeScript) + pool: + vmImage: ubuntu-18.04 + steps: + - template: templates/install-deps.yml + - template: templates/fabcar/azure-pipelines-typescript.yml + - job: TestNetwork displayName: Test Network pool: diff --git a/ci/templates/fabcar/azure-pipelines-typescript.yml b/ci/templates/fabcar/azure-pipelines-typescript.yml new file mode 100644 index 00000000..4e566f5b --- /dev/null +++ b/ci/templates/fabcar/azure-pipelines-typescript.yml @@ -0,0 +1,22 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# + +steps: + - script: ./startFabric.sh typescript + workingDirectory: fabcar + displayName: Start Fabric + - script: retry -- npm install + workingDirectory: fabcar/typescript + displayName: Install FabCar Application Dependencies + - script: npm run build + workingDirectory: fabcar/typescript + displayName: Build FabCar application + - script: | + set -ex + node dist/enrollAdmin + node dist/registerUser + node dist/invoke + node dist/query + workingDirectory: fabcar/typescript + displayName: Run FabCar Application diff --git a/ci/templates/test-network/azure-pipelines.yml b/ci/templates/test-network/azure-pipelines.yml index f2c28cda..e934455c 100644 --- a/ci/templates/test-network/azure-pipelines.yml +++ b/ci/templates/test-network/azure-pipelines.yml @@ -7,6 +7,7 @@ steps: ./network.sh up createChannel -s couchdb -i ${FABRIC_VERSION} # FABRIC_VERSION is set in ci/azure-pipelines.yml ./network.sh deployCC -ccn basic -ccv 1 -ccl javascript -cci initLedger ./network.sh deployCC -ccn basic -ccv 2 -ccl golang -cci initLedger + ./network.sh deployCC -ccn basic -ccv 3 -ccl typescript -cci initLedger ./network.sh deployCC -ccn secure -ccv 1 -ccl golang ./network.sh down workingDirectory: test-network diff --git a/commercial-paper/organization/digibank/contract/.npmignore b/commercial-paper/organization/digibank/contract/.npmignore index 9f444c23..a00ca941 100644 --- a/commercial-paper/organization/digibank/contract/.npmignore +++ b/commercial-paper/organization/digibank/contract/.npmignore @@ -40,6 +40,9 @@ build/Release node_modules/ jspm_packages/ +# TypeScript v1 declaration files +typings/ + # Optional npm cache directory .npm diff --git a/commercial-paper/organization/magnetocorp/contract/.npmignore b/commercial-paper/organization/magnetocorp/contract/.npmignore index 9f444c23..a00ca941 100644 --- a/commercial-paper/organization/magnetocorp/contract/.npmignore +++ b/commercial-paper/organization/magnetocorp/contract/.npmignore @@ -40,6 +40,9 @@ build/Release node_modules/ jspm_packages/ +# TypeScript v1 declaration files +typings/ + # Optional npm cache directory .npm diff --git a/fabcar/javascript/.gitignore b/fabcar/javascript/.gitignore index a610a823..1dcaf1f2 100644 --- a/fabcar/javascript/.gitignore +++ b/fabcar/javascript/.gitignore @@ -40,6 +40,9 @@ build/Release node_modules/ jspm_packages/ +# TypeScript v1 declaration files +typings/ + # Optional npm cache directory .npm diff --git a/fabcar/networkDown.sh b/fabcar/networkDown.sh index 74765e96..c1bc2313 100755 --- a/fabcar/networkDown.sh +++ b/fabcar/networkDown.sh @@ -15,3 +15,4 @@ popd # clean out any old identites in the wallets rm -rf javascript/wallet/* rm -rf java/wallet/* +rm -rf typescript/wallet/* diff --git a/fabcar/startFabric.sh b/fabcar/startFabric.sh index 88edaed6..8ce18cb3 100755 --- a/fabcar/startFabric.sh +++ b/fabcar/startFabric.sh @@ -19,15 +19,18 @@ elif [ "$CC_SRC_LANGUAGE" = "javascript" ]; then CC_SRC_PATH="../chaincode/fabcar/javascript/" elif [ "$CC_SRC_LANGUAGE" = "java" ]; then CC_SRC_PATH="../chaincode/fabcar/java" +elif [ "$CC_SRC_LANGUAGE" = "typescript" ]; then + CC_SRC_PATH="../chaincode/fabcar/typescript/" else echo The chaincode language ${CC_SRC_LANGUAGE} is not supported by this script - echo Supported chaincode languages are: go, java, and javascript + echo Supported chaincode languages are: go, java, javascript, and typescript exit 1 fi # clean out any old identites in the wallets rm -rf javascript/wallet/* rm -rf java/wallet/* +rm -rf typescript/wallet/* rm -rf go/wallet/* # launch network; create channel and join peer to channel @@ -67,6 +70,31 @@ JavaScript: return all cars, but you can update the application to evaluate other transactions: node query +TypeScript: + + Start by changing into the "typescript" directory: + cd typescript + + Next, install all required packages: + npm install + + Next, compile the TypeScript code into JavaScript: + npm run build + + Then run the following applications to enroll the admin user, and register a new user + called appUser which will be used by the other applications to interact with the deployed + FabCar contract: + node dist/enrollAdmin + node dist/registerUser + + You can run the invoke application as follows. By default, the invoke application will + create a new car, but you can update the application to submit other transactions: + node dist/invoke + + You can run the query application as follows. By default, the query application will + return all cars, but you can update the application to evaluate other transactions: + node dist/query + Java: Start by changing into the "java" directory: diff --git a/fabcar/typescript/.editorconfig b/fabcar/typescript/.editorconfig new file mode 100755 index 00000000..75a13be2 --- /dev/null +++ b/fabcar/typescript/.editorconfig @@ -0,0 +1,16 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# + +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/fabcar/typescript/.gitignore b/fabcar/typescript/.gitignore new file mode 100644 index 00000000..2a6c904c --- /dev/null +++ b/fabcar/typescript/.gitignore @@ -0,0 +1,83 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# next.js build output +.next + +# nuxt.js build output +.nuxt + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless + +# Compiled TypeScript files +dist + +wallet +!wallet/.gitkeep diff --git a/fabcar/typescript/package.json b/fabcar/typescript/package.json new file mode 100644 index 00000000..6cb67aa1 --- /dev/null +++ b/fabcar/typescript/package.json @@ -0,0 +1,59 @@ +{ + "name": "fabcar", + "version": "1.0.0", + "description": "FabCar application implemented in TypeScript", + "engines": { + "node": ">=8", + "npm": ">=5" + }, + "scripts": { + "lint": "tslint -c tslint.json 'src/**/*.ts'", + "pretest": "npm run lint", + "test": "nyc mocha -r ts-node/register src/**/*.spec.ts", + "build": "tsc", + "build:watch": "tsc -w", + "prepublishOnly": "npm run build" + }, + "engineStrict": true, + "author": "Hyperledger", + "license": "Apache-2.0", + "dependencies": { + "fabric-ca-client": "^2.1.0", + "fabric-network": "^2.1.0" + }, + "devDependencies": { + "@types/chai": "^4.1.7", + "@types/mocha": "^5.2.5", + "@types/node": "^10.12.10", + "@types/sinon": "^5.0.7", + "@types/sinon-chai": "^3.2.1", + "chai": "^4.2.0", + "mocha": "^5.2.0", + "nyc": "^14.1.1", + "sinon": "^7.1.1", + "sinon-chai": "^3.3.0", + "ts-node": "^7.0.1", + "tslint": "^5.11.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 + } +} diff --git a/fabcar/typescript/src/enrollAdmin.ts b/fabcar/typescript/src/enrollAdmin.ts new file mode 100644 index 00000000..e84139b8 --- /dev/null +++ b/fabcar/typescript/src/enrollAdmin.ts @@ -0,0 +1,52 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as FabricCAServices from 'fabric-ca-client'; +import { Wallets, X509Identity } from 'fabric-network'; +import * as fs from 'fs'; +import * as path from 'path'; + +async function main() { + try { + // load the network configuration + const ccpPath = path.resolve(__dirname, '..', '..', '..','test-network','organizations','peerOrganizations','org1.example.com', 'connection-org1.json'); + const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); + + // Create a new CA client for interacting with the CA. + const caInfo = ccp.certificateAuthorities['ca.org1.example.com']; + const caTLSCACerts = caInfo.tlsCACerts.pem; + const ca = new FabricCAServices(caInfo.url, { trustedRoots: caTLSCACerts, verify: false }, caInfo.caName); + + // Create a new file system based wallet for managing identities. + const walletPath = path.join(process.cwd(), 'wallet'); + const wallet = await Wallets.newFileSystemWallet(walletPath); + console.log(`Wallet path: ${walletPath}`); + + // Check to see if we've already enrolled the admin user. + const identity = await wallet.get('admin'); + if (identity) { + console.log('An identity for the admin user "admin" already exists in the wallet'); + return; + } + + // Enroll the admin user, and import the new identity into the wallet. + const enrollment = await ca.enroll({ enrollmentID: 'admin', enrollmentSecret: 'adminpw' }); + const x509Identity: X509Identity = { + credentials: { + certificate: enrollment.certificate, + privateKey: enrollment.key.toBytes(), + }, + mspId: 'Org1MSP', + type: 'X.509', + }; + await wallet.put('admin', x509Identity); + console.log('Successfully enrolled admin user "admin" and imported it into the wallet'); + + } catch (error) { + console.error(`Failed to enroll admin user "admin": ${error}`); + process.exit(1); + } +} + +main(); diff --git a/fabcar/typescript/src/invoke.ts b/fabcar/typescript/src/invoke.ts new file mode 100644 index 00000000..762263ed --- /dev/null +++ b/fabcar/typescript/src/invoke.ts @@ -0,0 +1,53 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Gateway, Wallets } from 'fabric-network'; +import * as path from 'path'; +import * as fs from 'fs'; + +async function main() { + try { + // load the network configuration + const ccpPath = path.resolve(__dirname, '..', '..', '..','test-network','organizations','peerOrganizations','org1.example.com', 'connection-org1.json'); + const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); + + // Create a new file system based wallet for managing identities. + const walletPath = path.join(process.cwd(), 'wallet'); + const wallet = await Wallets.newFileSystemWallet(walletPath); + console.log(`Wallet path: ${walletPath}`); + + // Check to see if we've already enrolled the user. + const identity = await wallet.get('appUser'); + if (!identity) { + console.log('An identity for the user "appUser" does not exist in the wallet'); + console.log('Run the registerUser.ts application before retrying'); + return; + } + + // Create a new gateway for connecting to our peer node. + const gateway = new Gateway(); + await gateway.connect(ccp, { wallet, identity: 'appUser', discovery: { enabled: true, asLocalhost: true } }); + + // Get the network (channel) our contract is deployed to. + const network = await gateway.getNetwork('mychannel'); + + // Get the contract from the network. + const contract = network.getContract('fabcar'); + + // Submit the specified transaction. + // createCar transaction - requires 5 argument, ex: ('createCar', 'CAR12', 'Honda', 'Accord', 'Black', 'Tom') + // changeCarOwner transaction - requires 2 args , ex: ('changeCarOwner', 'CAR12', 'Dave') + await contract.submitTransaction('createCar', 'CAR12', 'Honda', 'Accord', 'Black', 'Tom'); + console.log(`Transaction has been submitted`); + + // Disconnect from the gateway. + await gateway.disconnect(); + + } catch (error) { + console.error(`Failed to submit transaction: ${error}`); + process.exit(1); + } +} + +main(); diff --git a/fabcar/typescript/src/query.ts b/fabcar/typescript/src/query.ts new file mode 100644 index 00000000..b86677ee --- /dev/null +++ b/fabcar/typescript/src/query.ts @@ -0,0 +1,51 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Gateway, Wallets } from 'fabric-network'; +import * as path from 'path'; +import * as fs from 'fs'; + + +async function main() { + try { + // load the network configuration + const ccpPath = path.resolve(__dirname, '..', '..', '..','test-network','organizations','peerOrganizations','org1.example.com', 'connection-org1.json'); + const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); + + // Create a new file system based wallet for managing identities. + const walletPath = path.join(process.cwd(), 'wallet'); + const wallet = await Wallets.newFileSystemWallet(walletPath); + console.log(`Wallet path: ${walletPath}`); + + // Check to see if we've already enrolled the user. + const identity = await wallet.get('appUser'); + if (!identity) { + console.log('An identity for the user "appUser" does not exist in the wallet'); + console.log('Run the registerUser.ts application before retrying'); + return; + } + + // Create a new gateway for connecting to our peer node. + const gateway = new Gateway(); + await gateway.connect(ccp, { wallet, identity: 'appUser', discovery: { enabled: true, asLocalhost: true } }); + + // Get the network (channel) our contract is deployed to. + const network = await gateway.getNetwork('mychannel'); + + // Get the contract from the network. + const contract = network.getContract('fabcar'); + + // Evaluate the specified transaction. + // queryCar transaction - requires 1 argument, ex: ('queryCar', 'CAR4') + // queryAllCars transaction - requires no arguments, ex: ('queryAllCars') + const result = await contract.evaluateTransaction('queryAllCars'); + console.log(`Transaction has been evaluated, result is: ${result.toString()}`); + + } catch (error) { + console.error(`Failed to evaluate transaction: ${error}`); + process.exit(1); + } +} + +main(); diff --git a/fabcar/typescript/src/registerUser.ts b/fabcar/typescript/src/registerUser.ts new file mode 100644 index 00000000..45c07d3f --- /dev/null +++ b/fabcar/typescript/src/registerUser.ts @@ -0,0 +1,64 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Wallets, X509Identity } from 'fabric-network'; +import * as FabricCAServices from 'fabric-ca-client'; +import * as path from 'path'; +import * as fs from 'fs'; + +async function main() { + try { + // load the network configuration + const ccpPath = path.resolve(__dirname, '..', '..', '..','test-network','organizations','peerOrganizations','org1.example.com', 'connection-org1.json'); + let ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); + + // Create a new CA client for interacting with the CA. + const caURL = ccp.certificateAuthorities['ca.org1.example.com'].url; + const ca = new FabricCAServices(caURL); + + // Create a new file system based wallet for managing identities. + const walletPath = path.join(process.cwd(), 'wallet'); + const wallet = await Wallets.newFileSystemWallet(walletPath); + console.log(`Wallet path: ${walletPath}`); + + // Check to see if we've already enrolled the user. + const userIdentity = await wallet.get('appUser'); + if (userIdentity) { + console.log('An identity for the user "appUser" already exists in the wallet'); + return; + } + + // Check to see if we've already enrolled the admin user. + const adminIdentity = await wallet.get('admin'); + if (!adminIdentity) { + console.log('An identity for the admin user "admin" does not exist in the wallet'); + console.log('Run the enrollAdmin.ts application before retrying'); + return; + } + + // build a user object for authenticating with the CA + const provider = wallet.getProviderRegistry().getProvider(adminIdentity.type); + const adminUser = await provider.getUserContext(adminIdentity, 'admin'); + + // Register the user, enroll the user, and import the new identity into the wallet. + const secret = await ca.register({ affiliation: 'org1.department1', enrollmentID: 'appUser', role: 'client' }, adminUser); + const enrollment = await ca.enroll({ enrollmentID: 'appUser', enrollmentSecret: secret }); + const x509Identity: X509Identity = { + credentials: { + certificate: enrollment.certificate, + privateKey: enrollment.key.toBytes(), + }, + mspId: 'Org1MSP', + type: 'X.509', + }; + await wallet.put('appUser', x509Identity); + console.log('Successfully registered and enrolled admin user "appUser" and imported it into the wallet'); + + } catch (error) { + console.error(`Failed to register user "appUser": ${error}`); + process.exit(1); + } +} + +main(); diff --git a/fabcar/typescript/tsconfig.json b/fabcar/typescript/tsconfig.json new file mode 100644 index 00000000..8c96ea07 --- /dev/null +++ b/fabcar/typescript/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "outDir": "dist", + "target": "es2017", + "moduleResolution": "node", + "module": "commonjs", + "declaration": true, + "sourceMap": true + }, + "include": [ + "./src/**/*" + ], + "exclude": [ + "./src/**/*.spec.ts" + ] +} diff --git a/fabcar/typescript/tslint.json b/fabcar/typescript/tslint.json new file mode 100644 index 00000000..e08fd0b8 --- /dev/null +++ b/fabcar/typescript/tslint.json @@ -0,0 +1,22 @@ +{ + "defaultSeverity": "error", + "extends": [ + "tslint:recommended" + ], + "jsRules": {}, + "rules": { + "indent": [true, "spaces", 4], + "linebreak-style": [true, "LF"], + "quotemark": [true, "single"], + "semicolon": [true, "always"], + "no-console": false, + "curly": true, + "triple-equals": true, + "no-string-throw": true, + "no-var-keyword": true, + "no-trailing-whitespace": true, + "object-literal-key-quotes": [true, "as-needed"], + "max-line-length": false + }, + "rulesDirectory": [] +} diff --git a/fabcar/typescript/wallet/.gitkeep b/fabcar/typescript/wallet/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/test-network/network.sh b/test-network/network.sh index 68b5ff43..6a3eb33b 100755 --- a/test-network/network.sh +++ b/test-network/network.sh @@ -41,7 +41,7 @@ function printHelp() { echo " Used with "$'\e[0;32m'network.sh deployCC$'\e[0m' echo " -c - deploy chaincode to channel" echo " -ccn - the short name of the chaincode to deploy: basic (default),ledger, private, secured" - echo " -ccl - the programming language of the chaincode to deploy: go (default), java, and javascript" + echo " -ccl - the programming language of the chaincode to deploy: go (default), java, javascript, typescript" echo " -ccv - chaincode version. 1.0 (default)" echo " -ccs - chaincode definition sequence. Must be an integer, 1 (default), 2, 3, etc" echo " -ccp - Optional, path to the chaincode. When provided the -ccn will be used as the deployed name and not the short name of the known chaincodes." diff --git a/test-network/scripts/deployCC.sh b/test-network/scripts/deployCC.sh index 9ef8fe7f..daa434c6 100755 --- a/test-network/scripts/deployCC.sh +++ b/test-network/scripts/deployCC.sh @@ -62,6 +62,8 @@ if [ "$CC_SRC_PATH" = "NA" ]; then CC_SRC_PATH="$CC_SRC_PATH/chaincode-java/" elif [ "$CC_SRC_LANGUAGE" = "javascript" ]; then CC_SRC_PATH="$CC_SRC_PATH/chaincode-javascript/" + elif [ "$CC_SRC_LANGUAGE" = "typescript" ]; then + CC_SRC_PATH="$CC_SRC_PATH/chaincode-typescript/" fi # check that the language is available for the sample chaincode @@ -99,9 +101,19 @@ elif [ "$CC_SRC_LANGUAGE" = "java" ]; then elif [ "$CC_SRC_LANGUAGE" = "javascript" ]; then CC_RUNTIME_LANGUAGE=node +elif [ "$CC_SRC_LANGUAGE" = "typescript" ]; then + CC_RUNTIME_LANGUAGE=node + + echo Compiling TypeScript code into JavaScript ... + pushd $CC_SRC_PATH + npm install + npm run build + popd + echo Finished compiling TypeScript code into JavaScript + else echo The chaincode language ${CC_SRC_LANGUAGE} is not supported by this script - echo Supported chaincode languages are: go, java, and javascript + echo Supported chaincode languages are: go, java, javascript, and typescript exit 1 fi