mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-17 07:25:10 +00:00
This reverts commit 7a23d7872a.
This commit is contained in:
parent
7a23d7872a
commit
7771b4f216
48 changed files with 1508 additions and 5 deletions
|
|
@ -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** |
|
||||
|
|
|
|||
17
asset-transfer-basic/application-typescript/.gitignore
vendored
Normal file
17
asset-transfer-basic/application-typescript/.gitignore
vendored
Normal file
|
|
@ -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
|
||||
59
asset-transfer-basic/application-typescript/package.json
Normal file
59
asset-transfer-basic/application-typescript/package.json
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
59
asset-transfer-basic/application-typescript/src/invoke.ts
Normal file
59
asset-transfer-basic/application-typescript/src/invoke.ts
Normal file
|
|
@ -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();
|
||||
57
asset-transfer-basic/application-typescript/src/query.ts
Normal file
57
asset-transfer-basic/application-typescript/src/query.ts
Normal file
|
|
@ -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();
|
||||
|
|
@ -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();
|
||||
16
asset-transfer-basic/application-typescript/tsconfig.json
Normal file
16
asset-transfer-basic/application-typescript/tsconfig.json
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"target": "es2017",
|
||||
"moduleResolution": "node",
|
||||
"module": "commonjs",
|
||||
"declaration": true,
|
||||
"sourceMap": true
|
||||
},
|
||||
"include": [
|
||||
"./src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"./src/**/*.spec.ts"
|
||||
]
|
||||
}
|
||||
22
asset-transfer-basic/application-typescript/tslint.json
Normal file
22
asset-transfer-basic/application-typescript/tslint.json
Normal file
|
|
@ -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": []
|
||||
}
|
||||
16
asset-transfer-basic/chaincode-typescript/.gitignore
vendored
Normal file
16
asset-transfer-basic/chaincode-typescript/.gitignore
vendored
Normal file
|
|
@ -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
|
||||
|
||||
62
asset-transfer-basic/chaincode-typescript/package.json
Normal file
62
asset-transfer-basic/chaincode-typescript/package.json
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
12
asset-transfer-basic/chaincode-typescript/src/asset.ts
Normal file
12
asset-transfer-basic/chaincode-typescript/src/asset.ts
Normal file
|
|
@ -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;
|
||||
}
|
||||
153
asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts
Normal file
153
asset-transfer-basic/chaincode-typescript/src/assetTransfer.ts
Normal file
|
|
@ -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<string> {
|
||||
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<boolean> {
|
||||
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<string> {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
8
asset-transfer-basic/chaincode-typescript/src/index.ts
Normal file
8
asset-transfer-basic/chaincode-typescript/src/index.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { AssetTransfer } from './assetTransfer';
|
||||
export { AssetTransfer } from './assetTransfer';
|
||||
|
||||
export const contracts: any[] = [AssetTransfer];
|
||||
16
asset-transfer-basic/chaincode-typescript/tsconfig.json
Normal file
16
asset-transfer-basic/chaincode-typescript/tsconfig.json
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"target": "es2017",
|
||||
"moduleResolution": "node",
|
||||
"module": "commonjs",
|
||||
"declaration": true,
|
||||
"sourceMap": true
|
||||
},
|
||||
"include": [
|
||||
"./src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"./src/**/*.spec.ts"
|
||||
]
|
||||
}
|
||||
21
asset-transfer-basic/chaincode-typescript/tslint.json
Normal file
21
asset-transfer-basic/chaincode-typescript/tslint.json
Normal file
|
|
@ -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": []
|
||||
}
|
||||
|
|
@ -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 |
|
||||
|
|
|
|||
3
chaincode/abstore/javascript/.gitignore
vendored
3
chaincode/abstore/javascript/.gitignore
vendored
|
|
@ -40,6 +40,9 @@ build/Release
|
|||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
|
|
|
|||
3
chaincode/fabcar/javascript/.gitignore
vendored
3
chaincode/fabcar/javascript/.gitignore
vendored
|
|
@ -40,6 +40,9 @@ build/Release
|
|||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
|
|
|
|||
16
chaincode/fabcar/typescript/.editorconfig
Executable file
16
chaincode/fabcar/typescript/.editorconfig
Executable file
|
|
@ -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
|
||||
81
chaincode/fabcar/typescript/.gitignore
vendored
Normal file
81
chaincode/fabcar/typescript/.gitignore
vendored
Normal file
|
|
@ -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
|
||||
|
||||
62
chaincode/fabcar/typescript/package.json
Normal file
62
chaincode/fabcar/typescript/package.json
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
11
chaincode/fabcar/typescript/src/car.ts
Normal file
11
chaincode/fabcar/typescript/src/car.ts
Normal file
|
|
@ -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;
|
||||
}
|
||||
140
chaincode/fabcar/typescript/src/fabcar.ts
Normal file
140
chaincode/fabcar/typescript/src/fabcar.ts
Normal file
|
|
@ -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<string> {
|
||||
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<string> {
|
||||
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 ===========');
|
||||
}
|
||||
|
||||
}
|
||||
8
chaincode/fabcar/typescript/src/index.ts
Normal file
8
chaincode/fabcar/typescript/src/index.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { FabCar } from './fabcar';
|
||||
export { FabCar } from './fabcar';
|
||||
|
||||
export const contracts: any[] = [ FabCar ];
|
||||
16
chaincode/fabcar/typescript/tsconfig.json
Normal file
16
chaincode/fabcar/typescript/tsconfig.json
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"target": "es2017",
|
||||
"moduleResolution": "node",
|
||||
"module": "commonjs",
|
||||
"declaration": true,
|
||||
"sourceMap": true
|
||||
},
|
||||
"include": [
|
||||
"./src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"./src/**/*.spec.ts"
|
||||
]
|
||||
}
|
||||
21
chaincode/fabcar/typescript/tslint.json
Normal file
21
chaincode/fabcar/typescript/tslint.json
Normal file
|
|
@ -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": []
|
||||
}
|
||||
3
chaincode/marbles02/javascript/.gitignore
vendored
3
chaincode/marbles02/javascript/.gitignore
vendored
|
|
@ -40,6 +40,9 @@ build/Release
|
|||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
22
ci/templates/fabcar/azure-pipelines-typescript.yml
Normal file
22
ci/templates/fabcar/azure-pipelines-typescript.yml
Normal file
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -40,6 +40,9 @@ build/Release
|
|||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,9 @@ build/Release
|
|||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
|
|
|
|||
3
fabcar/javascript/.gitignore
vendored
3
fabcar/javascript/.gitignore
vendored
|
|
@ -40,6 +40,9 @@ build/Release
|
|||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
|
|
|
|||
|
|
@ -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/*
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
16
fabcar/typescript/.editorconfig
Executable file
16
fabcar/typescript/.editorconfig
Executable file
|
|
@ -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
|
||||
83
fabcar/typescript/.gitignore
vendored
Normal file
83
fabcar/typescript/.gitignore
vendored
Normal file
|
|
@ -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
|
||||
59
fabcar/typescript/package.json
Normal file
59
fabcar/typescript/package.json
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
52
fabcar/typescript/src/enrollAdmin.ts
Normal file
52
fabcar/typescript/src/enrollAdmin.ts
Normal file
|
|
@ -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();
|
||||
53
fabcar/typescript/src/invoke.ts
Normal file
53
fabcar/typescript/src/invoke.ts
Normal file
|
|
@ -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();
|
||||
51
fabcar/typescript/src/query.ts
Normal file
51
fabcar/typescript/src/query.ts
Normal file
|
|
@ -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();
|
||||
64
fabcar/typescript/src/registerUser.ts
Normal file
64
fabcar/typescript/src/registerUser.ts
Normal file
|
|
@ -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();
|
||||
16
fabcar/typescript/tsconfig.json
Normal file
16
fabcar/typescript/tsconfig.json
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"target": "es2017",
|
||||
"moduleResolution": "node",
|
||||
"module": "commonjs",
|
||||
"declaration": true,
|
||||
"sourceMap": true
|
||||
},
|
||||
"include": [
|
||||
"./src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"./src/**/*.spec.ts"
|
||||
]
|
||||
}
|
||||
22
fabcar/typescript/tslint.json
Normal file
22
fabcar/typescript/tslint.json
Normal file
|
|
@ -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": []
|
||||
}
|
||||
0
fabcar/typescript/wallet/.gitkeep
Normal file
0
fabcar/typescript/wallet/.gitkeep
Normal file
|
|
@ -41,7 +41,7 @@ function printHelp() {
|
|||
echo " Used with "$'\e[0;32m'network.sh deployCC$'\e[0m'
|
||||
echo " -c <channel name> - deploy chaincode to channel"
|
||||
echo " -ccn <name> - the short name of the chaincode to deploy: basic (default),ledger, private, secured"
|
||||
echo " -ccl <language> - the programming language of the chaincode to deploy: go (default), java, and javascript"
|
||||
echo " -ccl <language> - the programming language of the chaincode to deploy: go (default), java, javascript, typescript"
|
||||
echo " -ccv <version> - chaincode version. 1.0 (default)"
|
||||
echo " -ccs <sequence> - chaincode definition sequence. Must be an integer, 1 (default), 2, 3, etc"
|
||||
echo " -ccp <path> - 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."
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue