mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-21 09:05:10 +00:00
FAB-12322 Update commercial-paper sample
Change-Id: I235cb62df492b7713bb1c355b7457f679903bd34 Signed-off-by: Anthony O'Dowd <a_o-dowd@uk.ibm.com>
This commit is contained in:
parent
df311ce23a
commit
e67fcf14a3
47 changed files with 2086 additions and 522 deletions
8
commercial-paper/.gitignore
vendored
Normal file
8
commercial-paper/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
organization/magnetocorp/application/node_modules/
|
||||||
|
organization/magnetocorp/contract/node_modules/
|
||||||
|
organization/magnetocorp/identity/user/
|
||||||
|
organization/digibank/application/node_modules/
|
||||||
|
organization/digibank/contract/node_modules/
|
||||||
|
organization/digibank/identity/user/
|
||||||
|
package-lock.json
|
||||||
|
.vscode
|
||||||
|
|
@ -1,76 +0,0 @@
|
||||||
/*
|
|
||||||
SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This application has 6 basic steps:
|
|
||||||
* 1. Select an identity from a wallet
|
|
||||||
* 2. Connect to network gateway
|
|
||||||
* 3. Access PaperNet network
|
|
||||||
* 4. Construct request to issue commercial paper
|
|
||||||
* 5. Submit transaction
|
|
||||||
* 6. Process response
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
// Bring key classes into scope, most importantly Fabric SDK network class
|
|
||||||
const file = require("fs");
|
|
||||||
const yaml = require('js-yaml');
|
|
||||||
const { FileSystemWallet, Gateway } = require('fabric-network');
|
|
||||||
const { CommercialPaper } = require('./paper.js');
|
|
||||||
|
|
||||||
// A wallet stores a collection of identities for use
|
|
||||||
const wallet = new FileSystemWallet('./wallet');
|
|
||||||
|
|
||||||
// A gateway defines the peers used to access Fabric networks
|
|
||||||
const gateway = new Gateway();
|
|
||||||
|
|
||||||
// Main try/catch/finally block
|
|
||||||
try {
|
|
||||||
|
|
||||||
// Load connection profile; will be used to locate a gateway
|
|
||||||
connectionProfile = yaml.safeLoad(file.readFileSync('./gateway/connectionProfile.yaml', 'utf8'));
|
|
||||||
|
|
||||||
// Set connection options; use 'admin' identity from application wallet
|
|
||||||
let connectionOptions = {
|
|
||||||
identity: 'isabella.the.issuer@magnetocorp.com',
|
|
||||||
wallet: wallet,
|
|
||||||
commitTimeout: 100,
|
|
||||||
strategy: MSPID_SCOPE_ANYFORTX,
|
|
||||||
commitNotifyStrategy: WAIT_FOR_ALL_CHANNEL_PEER
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect to gateway using application specified parameters
|
|
||||||
await gateway.connect(connectionProfile, connectionOptions);
|
|
||||||
|
|
||||||
console.log('Connected to Fabric gateway.')
|
|
||||||
|
|
||||||
// Get addressability to PaperNet network
|
|
||||||
const network = await gateway.getNetwork('PaperNet');
|
|
||||||
|
|
||||||
// Get addressability to commercial paper contract
|
|
||||||
const contract = await network.getContract('papercontract', 'org.papernet.commercialpaper');
|
|
||||||
|
|
||||||
console.log('Submit commercial paper issue transaction.')
|
|
||||||
|
|
||||||
// issue commercial paper
|
|
||||||
const response = await contract.submitTransaction('issue', 'MagnetoCorp', '00001', '2020-05-31', '2020-11-30', '5000000');
|
|
||||||
|
|
||||||
let paper = CommercialPaper.deserialize(response);
|
|
||||||
|
|
||||||
console.log(`${paper.issuer} commercial paper : ${paper.paperNumber} successfully issued for value ${paper.faceValue}`);
|
|
||||||
|
|
||||||
console.log('Transaction complete.')
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
|
|
||||||
console.log(`Error processing transaction. ${error}`);
|
|
||||||
|
|
||||||
} finally {
|
|
||||||
|
|
||||||
// Disconnect from the gateway
|
|
||||||
console.log('Disconnect from Fabric gateway.')
|
|
||||||
gateway.disconnect();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
/*
|
|
||||||
SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
module.exports.contracts = require('./lib/cpcontract.js');
|
|
||||||
|
|
@ -1,113 +0,0 @@
|
||||||
/*
|
|
||||||
SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
// Smart contract API brought into scope
|
|
||||||
const {Contract} = require('fabric-contract-api');
|
|
||||||
|
|
||||||
// Commercial paper classes brought into scope
|
|
||||||
const {CommercialPaper, CommercialPaperList} = require('./cpstate.js');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define the commercial paper smart contract extending Fabric Contract class
|
|
||||||
*/
|
|
||||||
class CommercialPaperContract extends Contract {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Each smart contract can have a unique namespace; useful when multiple
|
|
||||||
* smart contracts per file.
|
|
||||||
* Use transaction context (ctx) to access list of all commercial papers.
|
|
||||||
*/
|
|
||||||
constructor() {
|
|
||||||
super('org.papernet.commercialpaper');
|
|
||||||
|
|
||||||
this.setBeforeFn = (ctx)=>{
|
|
||||||
ctx.cpList = new CommercialPaperList(ctx, 'COMMERCIALPAPER');
|
|
||||||
return ctx;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Issue commercial paper
|
|
||||||
* @param {TxContext} ctx the transaction context
|
|
||||||
* @param {String} issuer commercial paper issuer
|
|
||||||
* @param {Integer} paperNumber paper number for this issuer
|
|
||||||
* @param {String} issueDateTime paper issue date
|
|
||||||
* @param {String} maturityDateTime paper maturity date
|
|
||||||
* @param {Integer} faceValue face value of paper
|
|
||||||
*/
|
|
||||||
async issue(ctx, issuer, paperNumber, issueDateTime, maturityDateTime, faceValue) {
|
|
||||||
|
|
||||||
let cp = new CommercialPaper(issuer, paperNumber, issueDateTime, maturityDateTime, faceValue);
|
|
||||||
|
|
||||||
// {issuer:"MagnetoCorp", paperNumber:"00001", "May31 2020", "Nov 30 2020", "5M USD"}
|
|
||||||
|
|
||||||
await ctx.cpList.addPaper(cp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Buy commercial paper
|
|
||||||
* @param {TxContext} ctx the transaction context
|
|
||||||
* @param {String} issuer commercial paper issuer
|
|
||||||
* @param {Integer} paperNumber paper number for this issuer
|
|
||||||
* @param {String} currentOwner current owner of paper
|
|
||||||
* @param {String} newOwner new owner of paper
|
|
||||||
* @param {Integer} price price paid for this paper
|
|
||||||
* @param {String} purchaseDateTime time paper was purchased (i.e. traded)
|
|
||||||
*/
|
|
||||||
async buy(ctx, issuer, paperNumber, currentOwner, newOwner, price, purchaseDateTime) {
|
|
||||||
|
|
||||||
let cpKey = CommercialPaper.createKey(issuer, paperNumber);
|
|
||||||
let cp = await ctx.cpList.getPaper(cpKey);
|
|
||||||
|
|
||||||
if (cp.getOwner() !== currentOwner) {
|
|
||||||
throw new Error('Paper '+issuer+paperNumber+' is not owned by '+currentOwner);
|
|
||||||
}
|
|
||||||
// First buy moves state from ISSUED to TRADING
|
|
||||||
if (cp.isIssued()) {
|
|
||||||
cp.setTrading();
|
|
||||||
}
|
|
||||||
// Check paper is TRADING, not REDEEMED
|
|
||||||
if (cp.IsTrading()) {
|
|
||||||
cp.setOwner(newOwner);
|
|
||||||
} else {
|
|
||||||
throw new Error('Paper '+issuer+paperNumber+' is not trading. Current state = '+cp.getCurrentState());
|
|
||||||
}
|
|
||||||
|
|
||||||
await ctx.cpList.updatePaper(cp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Redeem commercial paper
|
|
||||||
* @param {TxContext} ctx the transaction context
|
|
||||||
* @param {String} issuer commercial paper issuer
|
|
||||||
* @param {Integer} paperNumber paper number for this issuer
|
|
||||||
* @param {String} redeemingOwner redeeming owner of paper
|
|
||||||
* @param {String} redeemDateTime time paper was redeemed
|
|
||||||
*/
|
|
||||||
async redeem(ctx, issuer, paperNumber, redeemingOwner, redeemDateTime) {
|
|
||||||
|
|
||||||
let cpKey = CommercialPaper.createKey(issuer, paperNumber);
|
|
||||||
let cp = await ctx.cpList.getPaper(cpKey);
|
|
||||||
|
|
||||||
// Check paper is TRADING, not REDEEMED
|
|
||||||
if (cp.IsRedeemed()) {
|
|
||||||
throw new Error('Paper '+issuer+paperNumber+' already redeemed');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that the redeemer owns the commercial paper before redeeming it
|
|
||||||
if (cp.getOwner() === redeemingOwner) {
|
|
||||||
cp.setOwner(cp.getIssuer());
|
|
||||||
cp.setRedeemed();
|
|
||||||
} else {
|
|
||||||
throw new Error('Redeeming owner does not own paper'+issuer+paperNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
await ctx.cpList.updatePaper(cp);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = CommericalPaperContract;
|
|
||||||
|
|
@ -1,148 +0,0 @@
|
||||||
/*
|
|
||||||
SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
// Helpful utilities class
|
|
||||||
const Utils = require('./utils.js');
|
|
||||||
|
|
||||||
// Enumeration of commercial paper state values
|
|
||||||
const cpState = {
|
|
||||||
ISSUED: 1,
|
|
||||||
TRADING: 2,
|
|
||||||
REDEEMED: 3
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CommercialPaper class defines a commercial paper state
|
|
||||||
*/
|
|
||||||
class CommercialPaper {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a commercial paper. Initial state is issued.
|
|
||||||
*/
|
|
||||||
constructor(issuer, paperNumber, issueDateTime, maturityDateTime, faceValue) {
|
|
||||||
this.issuer = issuer;
|
|
||||||
this.paperNumber = paperNumber;
|
|
||||||
this.owner = issuer;
|
|
||||||
this.issueDateTime = issueDateTime;
|
|
||||||
this.maturityDateTime = maturityDateTime;
|
|
||||||
this.faceValue = faceValue;
|
|
||||||
this.currentState = cpState.ISSUED;
|
|
||||||
this.key = CommercialPaper.createKey(issuer, paperNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The commercial paper is uniquely identified by its key.
|
|
||||||
* The key is a simple composite of issuer and paper number as strings.
|
|
||||||
*/
|
|
||||||
static createKey(issuer, paperNumber) {
|
|
||||||
return JSON.stringify(issuer) + JSON.stringify(paperNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Basic getters and setters
|
|
||||||
*/
|
|
||||||
getKey() {
|
|
||||||
return this.key;
|
|
||||||
}
|
|
||||||
|
|
||||||
getIssuer() {
|
|
||||||
return this.issuer;
|
|
||||||
}
|
|
||||||
|
|
||||||
setIssuer(newIssuer) {
|
|
||||||
this.issuer = newIssuer;
|
|
||||||
}
|
|
||||||
|
|
||||||
getOwner() {
|
|
||||||
return this.owner;
|
|
||||||
}
|
|
||||||
|
|
||||||
setOwner(newOwner) {
|
|
||||||
this.owner = newOwner;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Useful methods to encapsulate commercial paper states
|
|
||||||
*/
|
|
||||||
setTrading() {
|
|
||||||
this.currentState = cpState.TRADING;
|
|
||||||
}
|
|
||||||
|
|
||||||
setRedeemed() {
|
|
||||||
this.currentState = cpState.REDEEMED;
|
|
||||||
}
|
|
||||||
|
|
||||||
isTrading() {
|
|
||||||
return this.currentState === cpState.TRADING;
|
|
||||||
}
|
|
||||||
|
|
||||||
isRedeemed() {
|
|
||||||
return this.currentState === cpState.REDEEMED;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CommercialPaperList provides a virtual container to access all
|
|
||||||
* commercial papers. Each paper has unique key which associates it
|
|
||||||
* with the container, rather than the container containing a link to
|
|
||||||
* the paper. This is important in Fabric becuase it minimizes
|
|
||||||
* collisions for parallel transactions on different papers.
|
|
||||||
*/
|
|
||||||
class CommercialPaperList {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For this sample, it is sufficient to create a commercial paper list
|
|
||||||
* using a fixed container prefix. The transaction context is saved to
|
|
||||||
* access Fabric APIs when required.
|
|
||||||
*/
|
|
||||||
constructor(ctx, prefix) {
|
|
||||||
this.api = ctx.stub;
|
|
||||||
this.prefix = prefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a paper to the list. Creates a new state in worldstate with
|
|
||||||
* appropriate composite key. Note that paper defines its own key.
|
|
||||||
* Paper object is serialized before writing.
|
|
||||||
*/
|
|
||||||
async addPaper(cp) {
|
|
||||||
let key = this.api.createCompositeKey(this.prefix, [cp.getKey()]);
|
|
||||||
let data = Utils.serialize(cp);
|
|
||||||
await this.api.putState(key, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a paper from the list using issuer and paper number. Forms composite
|
|
||||||
* keys to retrieve data from world state. State data is deserialized
|
|
||||||
* into paper object before being returned.
|
|
||||||
*/
|
|
||||||
async getPaper(key) {
|
|
||||||
let key = this.api.createCompositeKey(this.prefix, [key]);
|
|
||||||
let data = await this.api.getState(key);
|
|
||||||
let cp = Utils.deserialize(data);
|
|
||||||
return cp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update a paper in the list. Puts the new state in world state with
|
|
||||||
* appropriate composite key. Note that paper defines its own key.
|
|
||||||
* Paper object is serialized before writing. Logic is very similar to
|
|
||||||
* addPaper() but kept separate becuase it is semantically distinct, and
|
|
||||||
* may change.
|
|
||||||
*/
|
|
||||||
async updatePaper(cp) {
|
|
||||||
let key = this.api.createCompositeKey(this.prefix, [cp.getKey()]);
|
|
||||||
let data = Utils.serialize(cp);
|
|
||||||
await this.api.putState(key, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
CommercialPaper,
|
|
||||||
CommercialPaperList
|
|
||||||
};
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
/*
|
|
||||||
SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility class for data, object mapulation, e.g. serialization
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Utils {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert object to buffer containing JSON data serialization
|
|
||||||
* Typically used before putState() ledger API
|
|
||||||
* @param {Object} object object to serialize
|
|
||||||
* @return {buffer} buffer with the data to store
|
|
||||||
*/
|
|
||||||
static serialize(object){
|
|
||||||
return Buffer.from(JSON.stringify(object));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deserialize object, i.e. Covert serialized data to JSON object
|
|
||||||
* Typically used after getState() ledger API
|
|
||||||
* @param {Object} data object to deserialize
|
|
||||||
* @return {json} json with the data to store
|
|
||||||
*/
|
|
||||||
static deserialize(data){
|
|
||||||
return JSON.parse(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Utils;
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
env: {
|
||||||
|
node: true,
|
||||||
|
mocha: true
|
||||||
|
},
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 8,
|
||||||
|
sourceType: 'script'
|
||||||
|
},
|
||||||
|
extends: "eslint:recommended",
|
||||||
|
rules: {
|
||||||
|
indent: ['error', 4],
|
||||||
|
'linebreak-style': ['error', 'unix'],
|
||||||
|
quotes: ['error', 'single'],
|
||||||
|
semi: ['error', 'always'],
|
||||||
|
'no-unused-vars': ['error', { args: 'none' }],
|
||||||
|
'no-console': 'off',
|
||||||
|
curly: 'error',
|
||||||
|
eqeqeq: 'error',
|
||||||
|
'no-throw-literal': 'error',
|
||||||
|
strict: 'error',
|
||||||
|
'no-var': 'error',
|
||||||
|
'dot-notation': 'error',
|
||||||
|
'no-tabs': 'error',
|
||||||
|
'no-trailing-spaces': 'error',
|
||||||
|
'no-use-before-define': 'error',
|
||||||
|
'no-useless-call': 'error',
|
||||||
|
'no-with': 'error',
|
||||||
|
'operator-linebreak': 'error',
|
||||||
|
yoda: 'error',
|
||||||
|
'quote-props': ['error', 'as-needed']
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Bring key classes into scope, most importantly Fabric SDK network class
|
||||||
|
const fs = require('fs');
|
||||||
|
const { FileSystemWallet, X509WalletMixin } = require('fabric-network');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const fixtures = path.resolve(__dirname, '../../../../basic-network');
|
||||||
|
|
||||||
|
// A wallet stores a collection of identities
|
||||||
|
const wallet = new FileSystemWallet('../identity/user/balaji/wallet');
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
|
||||||
|
// Main try/catch block
|
||||||
|
try {
|
||||||
|
|
||||||
|
// Identity to credentials to be stored in the wallet
|
||||||
|
const credPath = path.join(fixtures, '/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com');
|
||||||
|
const cert = fs.readFileSync(path.join(credPath, '/msp/signcerts/Admin@org1.example.com-cert.pem')).toString();
|
||||||
|
const key = fs.readFileSync(path.join(credPath, '/msp/keystore/cd96d5260ad4757551ed4a5a991e62130f8008a0bf996e4e4b84cd097a747fec_sk')).toString();
|
||||||
|
|
||||||
|
// Load credentials into wallet
|
||||||
|
const identityLabel = 'Admin@org1.example.com';
|
||||||
|
const identity = X509WalletMixin.createIdentity('Org1MSP', cert, key);
|
||||||
|
|
||||||
|
await wallet.import(identityLabel, identity);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.log(`Error adding to wallet. ${error}`);
|
||||||
|
console.log(error.stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main().then(() => {
|
||||||
|
console.log('done');
|
||||||
|
}).catch((e) => {
|
||||||
|
console.log(e);
|
||||||
|
console.log(e.stack);
|
||||||
|
process.exit(-1);
|
||||||
|
});
|
||||||
102
commercial-paper/organization/digibank/application/buy.js
Normal file
102
commercial-paper/organization/digibank/application/buy.js
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This application has 6 basic steps:
|
||||||
|
* 1. Select an identity from a wallet
|
||||||
|
* 2. Connect to network gateway
|
||||||
|
* 3. Access PaperNet network
|
||||||
|
* 4. Construct request to issue commercial paper
|
||||||
|
* 5. Submit transaction
|
||||||
|
* 6. Process response
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Bring key classes into scope, most importantly Fabric SDK network class
|
||||||
|
const fs = require('fs');
|
||||||
|
const yaml = require('js-yaml');
|
||||||
|
const { FileSystemWallet, Gateway } = require('fabric-network');
|
||||||
|
const CommercialPaper = require('../contract/lib/paper.js');
|
||||||
|
|
||||||
|
// A wallet stores a collection of identities for use
|
||||||
|
const wallet = new FileSystemWallet('../identity/user/balaji/wallet');
|
||||||
|
|
||||||
|
// Main program function
|
||||||
|
async function main() {
|
||||||
|
|
||||||
|
// A gateway defines the peers used to access Fabric networks
|
||||||
|
const gateway = new Gateway();
|
||||||
|
|
||||||
|
// Main try/catch block
|
||||||
|
try {
|
||||||
|
|
||||||
|
// Specify userName for network access
|
||||||
|
// const userName = 'isabella.issuer@magnetocorp.com';
|
||||||
|
const userName = 'Admin@org1.example.com';
|
||||||
|
|
||||||
|
// Load connection profile; will be used to locate a gateway
|
||||||
|
let connectionProfile = yaml.safeLoad(fs.readFileSync('../gateway/networkConnection.yaml', 'utf8'));
|
||||||
|
|
||||||
|
// Set connection options; identity and wallet
|
||||||
|
let connectionOptions = {
|
||||||
|
identity: userName,
|
||||||
|
wallet: wallet,
|
||||||
|
discovery: { enabled:false, asLocalhost: true }
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// Connect to gateway using application specified parameters
|
||||||
|
console.log('Connect to Fabric gateway.');
|
||||||
|
|
||||||
|
await gateway.connect(connectionProfile, connectionOptions);
|
||||||
|
|
||||||
|
// Access PaperNet network
|
||||||
|
console.log('Use network channel: mychannel.');
|
||||||
|
|
||||||
|
const network = await gateway.getNetwork('mychannel');
|
||||||
|
|
||||||
|
// Get addressability to commercial paper contract
|
||||||
|
console.log('Use org.papernet.commercialpaper smart contract.');
|
||||||
|
|
||||||
|
const contract = await network.getContract('papercontract', 'org.papernet.commercialpaper');
|
||||||
|
|
||||||
|
// buy commercial paper
|
||||||
|
console.log('Submit commercial paper buy transaction.');
|
||||||
|
|
||||||
|
const buyResponse = await contract.submitTransaction('buy', 'MagnetoCorp', '00001', 'MagnetoCorp', 'DigiBank', '4900000', '2020-05-31');
|
||||||
|
|
||||||
|
// process response
|
||||||
|
console.log('Process buy transaction response.');
|
||||||
|
|
||||||
|
let paper = CommercialPaper.fromBuffer(buyResponse);
|
||||||
|
|
||||||
|
console.log(`${paper.issuer} commercial paper : ${paper.paperNumber} successfully purchased by ${paper.owner}`);
|
||||||
|
console.log('Transaction complete.');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
|
||||||
|
console.log(`Error processing transaction. ${error}`);
|
||||||
|
console.log(error.stack);
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
|
||||||
|
// Disconnect from the gateway
|
||||||
|
console.log('Disconnect from Fabric gateway.')
|
||||||
|
gateway.disconnect();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
main().then(() => {
|
||||||
|
|
||||||
|
console.log('Buy program complete.');
|
||||||
|
|
||||||
|
}).catch((e) => {
|
||||||
|
|
||||||
|
console.log('Buy program exception.');
|
||||||
|
console.log(e);
|
||||||
|
console.log(e.stack);
|
||||||
|
process.exit(-1);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"name": "nodejs",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "buy.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "rm -rf _idwallet && node addToWallet.js && node buy.js"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"fabric-network": "^1.4.0-beta",
|
||||||
|
"fabric-client": "^1.4.0-beta",
|
||||||
|
"js-yaml": "^3.12.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"eslint": "^5.6.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
101
commercial-paper/organization/digibank/application/redeem.js
Normal file
101
commercial-paper/organization/digibank/application/redeem.js
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This application has 6 basic steps:
|
||||||
|
* 1. Select an identity from a wallet
|
||||||
|
* 2. Connect to network gateway
|
||||||
|
* 3. Access PaperNet network
|
||||||
|
* 4. Construct request to issue commercial paper
|
||||||
|
* 5. Submit transaction
|
||||||
|
* 6. Process response
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Bring key classes into scope, most importantly Fabric SDK network class
|
||||||
|
const fs = require('fs');
|
||||||
|
const yaml = require('js-yaml');
|
||||||
|
const { FileSystemWallet, Gateway } = require('fabric-network');
|
||||||
|
const CommercialPaper = require('../contract/lib/paper.js');
|
||||||
|
|
||||||
|
// A wallet stores a collection of identities for use
|
||||||
|
const wallet = new FileSystemWallet('../identity/user/balaji/wallet');
|
||||||
|
|
||||||
|
// Main program function
|
||||||
|
async function main() {
|
||||||
|
|
||||||
|
// A gateway defines the peers used to access Fabric networks
|
||||||
|
const gateway = new Gateway();
|
||||||
|
|
||||||
|
// Main try/catch block
|
||||||
|
try {
|
||||||
|
|
||||||
|
// Specify userName for network access
|
||||||
|
// const userName = 'isabella.issuer@magnetocorp.com';
|
||||||
|
const userName = 'Admin@org1.example.com';
|
||||||
|
|
||||||
|
// Load connection profile; will be used to locate a gateway
|
||||||
|
let connectionProfile = yaml.safeLoad(fs.readFileSync('../gateway/networkConnection.yaml', 'utf8'));
|
||||||
|
|
||||||
|
// Set connection options; identity and wallet
|
||||||
|
let connectionOptions = {
|
||||||
|
identity: userName,
|
||||||
|
wallet: wallet,
|
||||||
|
discovery: { enabled:false, asLocalhost: true }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Connect to gateway using application specified parameters
|
||||||
|
console.log('Connect to Fabric gateway.');
|
||||||
|
|
||||||
|
await gateway.connect(connectionProfile, connectionOptions);
|
||||||
|
|
||||||
|
// Access PaperNet network
|
||||||
|
console.log('Use network channel: mychannel.');
|
||||||
|
|
||||||
|
const network = await gateway.getNetwork('mychannel');
|
||||||
|
|
||||||
|
// Get addressability to commercial paper contract
|
||||||
|
console.log('Use org.papernet.commercialpaper smart contract.');
|
||||||
|
|
||||||
|
const contract = await network.getContract('papercontract', 'org.papernet.commercialpaper');
|
||||||
|
|
||||||
|
// redeem commercial paper
|
||||||
|
console.log('Submit commercial paper redeem transaction.');
|
||||||
|
|
||||||
|
const redeemResponse = await contract.submitTransaction('redeem', 'MagnetoCorp', '00001', 'DigiBank', '2020-11-30');
|
||||||
|
|
||||||
|
// process response
|
||||||
|
console.log('Process redeem transaction response.');
|
||||||
|
|
||||||
|
let paper = CommercialPaper.fromBuffer(redeemResponse);
|
||||||
|
|
||||||
|
console.log(`${paper.issuer} commercial paper : ${paper.paperNumber} successfully redeemed with ${paper.owner}`);
|
||||||
|
console.log('Transaction complete.');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
|
||||||
|
console.log(`Error processing transaction. ${error}`);
|
||||||
|
console.log(error.stack);
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
|
||||||
|
// Disconnect from the gateway
|
||||||
|
console.log('Disconnect from Fabric gateway.')
|
||||||
|
gateway.disconnect();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
main().then(() => {
|
||||||
|
|
||||||
|
console.log('Redeem program complete.');
|
||||||
|
|
||||||
|
}).catch((e) => {
|
||||||
|
|
||||||
|
console.log('Redeem program exception.');
|
||||||
|
console.log(e);
|
||||||
|
console.log(e.stack);
|
||||||
|
process.exit(-1);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
#
|
||||||
|
# Copyright IBM Corp All Rights Reserved
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
version: '2'
|
||||||
|
|
||||||
|
networks:
|
||||||
|
basic:
|
||||||
|
external:
|
||||||
|
name: net_basic
|
||||||
|
|
||||||
|
services:
|
||||||
|
cliDigiBank:
|
||||||
|
container_name: cliDigiBank
|
||||||
|
image: hyperledger/fabric-tools
|
||||||
|
tty: true
|
||||||
|
environment:
|
||||||
|
- GOPATH=/opt/gopath
|
||||||
|
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
|
||||||
|
- CORE_LOGGING_LEVEL=info
|
||||||
|
- CORE_PEER_ID=cli
|
||||||
|
- CORE_PEER_ADDRESS=peer0.org1.example.com:7051
|
||||||
|
- CORE_PEER_LOCALMSPID=Org1MSP
|
||||||
|
- CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
|
||||||
|
- CORE_CHAINCODE_KEEPALIVE=10
|
||||||
|
working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
|
||||||
|
command: /bin/bash
|
||||||
|
volumes:
|
||||||
|
- /var/run/:/host/var/run/
|
||||||
|
- ./../../../../organization/digibank:/opt/gopath/src/github.com/
|
||||||
|
- ./../../../../../basic-network/crypto-config:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/
|
||||||
|
networks:
|
||||||
|
- basic
|
||||||
|
#depends_on:
|
||||||
|
# - orderer.example.com
|
||||||
|
# - peer0.org1.example.com
|
||||||
|
# - couchdb
|
||||||
25
commercial-paper/organization/digibank/configuration/cli/monitordocker.sh
Executable file
25
commercial-paper/organization/digibank/configuration/cli/monitordocker.sh
Executable file
|
|
@ -0,0 +1,25 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# This script uses the logspout and http stream tools to let you watch the docker containers
|
||||||
|
# in action.
|
||||||
|
#
|
||||||
|
# More information at https://github.com/gliderlabs/logspout/tree/master/httpstream
|
||||||
|
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
DOCKER_NETWORK=basicnetwork_basic
|
||||||
|
else
|
||||||
|
DOCKER_NETWORK="$1"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo Starting monitoring on all containers on the network ${DOCKER_NETWORK}
|
||||||
|
|
||||||
|
docker kill logspout 2> /dev/null 1>&2 || true
|
||||||
|
docker rm logspout 2> /dev/null 1>&2 || true
|
||||||
|
|
||||||
|
docker run -d --name="logspout" \
|
||||||
|
--volume=/var/run/docker.sock:/var/run/docker.sock \
|
||||||
|
--publish=127.0.0.1:8000:80 \
|
||||||
|
--network ${DOCKER_NETWORK} \
|
||||||
|
gliderlabs/logspout
|
||||||
|
sleep 3
|
||||||
|
curl http://127.0.0.1:8000/logs
|
||||||
8
commercial-paper/organization/digibank/contract/index.js
Normal file
8
commercial-paper/organization/digibank/contract/index.js
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
/*
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const cpcontract = require('./lib/papercontract.js');
|
||||||
|
module.exports.contracts = [cpcontract];
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State class. States have a class, unique key, and a lifecycle current state
|
||||||
|
* the current state is determined by the specific subclass
|
||||||
|
*/
|
||||||
|
class State {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {String|Object} class An indentifiable class of the instance
|
||||||
|
* @param {keyParts[]} elements to pull together to make a key for the objects
|
||||||
|
*/
|
||||||
|
constructor(stateClass, keyParts) {
|
||||||
|
this.class = stateClass;
|
||||||
|
this.key = State.makeKey(keyParts);
|
||||||
|
this.currentState = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getClass() {
|
||||||
|
return this.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
getKey() {
|
||||||
|
return this.key;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSplitKey(){
|
||||||
|
return State.splitKey(this.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentState(){
|
||||||
|
return this.currentState;
|
||||||
|
}
|
||||||
|
|
||||||
|
serialize() {
|
||||||
|
return State.serialize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert object to buffer containing JSON data serialization
|
||||||
|
* Typically used before putState()ledger API
|
||||||
|
* @param {Object} JSON object to serialize
|
||||||
|
* @return {buffer} buffer with the data to store
|
||||||
|
*/
|
||||||
|
static serialize(object) {
|
||||||
|
return Buffer.from(JSON.stringify(object));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize object into one of a set of supported JSON classes
|
||||||
|
* i.e. Covert serialized data to JSON object
|
||||||
|
* Typically used after getState() ledger API
|
||||||
|
* @param {data} data to deserialize into JSON object
|
||||||
|
* @param (supportedClasses) the set of classes data can be serialized to
|
||||||
|
* @return {json} json with the data to store
|
||||||
|
*/
|
||||||
|
static deserialize(data, supportedClasses) {
|
||||||
|
let json = JSON.parse(data.toString());
|
||||||
|
let objClass = supportedClasses[json.class];
|
||||||
|
if (!objClass) {
|
||||||
|
throw new Error(`Unknown class of ${json.class}`);
|
||||||
|
}
|
||||||
|
let object = new (objClass)(json);
|
||||||
|
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize object into specific object class
|
||||||
|
* Typically used after getState() ledger API
|
||||||
|
* @param {data} data to deserialize into JSON object
|
||||||
|
* @return {json} json with the data to store
|
||||||
|
*/
|
||||||
|
static deserializeClass(data, objClass) {
|
||||||
|
let json = JSON.parse(data.toString());
|
||||||
|
let object = new (objClass)(json);
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Join the keyParts to make a unififed string
|
||||||
|
* @param (String[]) keyParts
|
||||||
|
*/
|
||||||
|
static makeKey(keyParts) {
|
||||||
|
return keyParts.map(part => JSON.stringify(part)).join(':');
|
||||||
|
}
|
||||||
|
|
||||||
|
static splitKey(key){
|
||||||
|
return key.split(':');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = State;
|
||||||
|
|
@ -3,32 +3,7 @@ SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
const State = require('./state.js');
|
||||||
/**
|
|
||||||
* Utility class for data, object mapulation, e.g. serialization
|
|
||||||
*/
|
|
||||||
class Utils {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert object to buffer containing JSON data serialization
|
|
||||||
* Typically used before putState()ledger API
|
|
||||||
* @param {Object} JSON object to serialize
|
|
||||||
* @return {buffer} buffer with the data to store
|
|
||||||
*/
|
|
||||||
static serialize(object) {
|
|
||||||
return Buffer.from(JSON.stringify(object));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deserialize object, i.e. Covert serialized data to JSON object
|
|
||||||
* Typically used after getState() ledger API
|
|
||||||
* @param {data} data to deserialize into JSON object
|
|
||||||
* @return {json} json with the data to store
|
|
||||||
*/
|
|
||||||
static deserialize(data) {
|
|
||||||
return JSON.parse(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* StateList provides a named virtual container for a set of ledger states.
|
* StateList provides a named virtual container for a set of ledger states.
|
||||||
|
|
@ -44,6 +19,8 @@ class StateList {
|
||||||
constructor(ctx, listName) {
|
constructor(ctx, listName) {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
this.name = listName;
|
this.name = listName;
|
||||||
|
this.supportedClasses = {};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -52,8 +29,8 @@ class StateList {
|
||||||
* State object is serialized before writing.
|
* State object is serialized before writing.
|
||||||
*/
|
*/
|
||||||
async addState(state) {
|
async addState(state) {
|
||||||
let key = this.ctx.stub.createCompositeKey(this.name, [state.getKey()]);
|
let key = this.ctx.stub.createCompositeKey(this.name, state.getSplitKey());
|
||||||
let data = Utils.serialize(state);
|
let data = State.serialize(state);
|
||||||
await this.ctx.stub.putState(key, data);
|
await this.ctx.stub.putState(key, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -62,10 +39,10 @@ class StateList {
|
||||||
* keys to retrieve state from world state. State data is deserialized
|
* keys to retrieve state from world state. State data is deserialized
|
||||||
* into JSON object before being returned.
|
* into JSON object before being returned.
|
||||||
*/
|
*/
|
||||||
async getState([keys]) {
|
async getState(key) {
|
||||||
let key = this.ctx.stub.createCompositeKey(this.name, [keys]);
|
let ledgerKey = this.ctx.stub.createCompositeKey(this.name, State.splitKey(key));
|
||||||
let data = await this.ctx.stub.getState(key);
|
let data = await this.ctx.stub.getState(ledgerKey);
|
||||||
let state = Utils.deserialize(data);
|
let state = State.deserialize(data, this.supportedClasses);
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,13 +53,16 @@ class StateList {
|
||||||
* addState() but kept separate becuase it is semantically distinct.
|
* addState() but kept separate becuase it is semantically distinct.
|
||||||
*/
|
*/
|
||||||
async updateState(state) {
|
async updateState(state) {
|
||||||
let key = this.ctx.stub.createCompositeKey(this.name, [state.getKey()]);
|
let key = this.ctx.stub.createCompositeKey(this.name, state.getSplitKey());
|
||||||
let data = Utils.serialize(state);
|
let data = State.serialize(state);
|
||||||
await this.ctx.stub.putState(key, data);
|
await this.ctx.stub.putState(key, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Stores the class for future deserialization */
|
||||||
|
use(stateClass) {
|
||||||
|
this.supportedClasses[stateClass.getClass()] = stateClass;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = StateList;
|
||||||
StateList
|
|
||||||
};
|
|
||||||
|
|
@ -4,6 +4,9 @@ SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
// Utility class for ledger state
|
||||||
|
const State = require('./../ledger-api/state.js');
|
||||||
|
|
||||||
// Enumerate commercial paper state values
|
// Enumerate commercial paper state values
|
||||||
const cpState = {
|
const cpState = {
|
||||||
ISSUED: 1,
|
ISSUED: 1,
|
||||||
|
|
@ -11,45 +14,15 @@ const cpState = {
|
||||||
REDEEMED: 3
|
REDEEMED: 3
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* State class. States have a type, unique key, and a lifecycle current state
|
|
||||||
*/
|
|
||||||
class State {
|
|
||||||
constructor(type, [keyParts]) {
|
|
||||||
this.type = JSON.stringify(type);
|
|
||||||
this.key = makeKey([keyParts]);
|
|
||||||
this.currentState = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
getType() {
|
|
||||||
return this.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
static makeKey([keyParts]) {
|
|
||||||
return keyParts.map(part => JSON.stringify(part)).join('');
|
|
||||||
}
|
|
||||||
|
|
||||||
getKey() {
|
|
||||||
return this.key;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CommercialPaper class extends State class
|
* CommercialPaper class extends State class
|
||||||
* Class will be used by application and smart contract to define a paper
|
* Class will be used by application and smart contract to define a paper
|
||||||
*/
|
*/
|
||||||
class CommercialPaper extends State {
|
class CommercialPaper extends State {
|
||||||
|
|
||||||
constructor(issuer, paperNumber, issueDateTime, maturityDateTime, faceValue) {
|
constructor(obj) {
|
||||||
super(`org.papernet.commercialpaper`, [issuer, paperNumber]);
|
super(CommercialPaper.getClass(), [obj.issuer, obj.paperNumber]);
|
||||||
|
Object.assign(this, obj);
|
||||||
this.issuer = issuer;
|
|
||||||
this.paperNumber = paperNumber;
|
|
||||||
this.owner = issuer;
|
|
||||||
this.issueDateTime = issueDateTime;
|
|
||||||
this.maturityDateTime = maturityDateTime;
|
|
||||||
this.faceValue = faceValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -98,20 +71,32 @@ class CommercialPaper extends State {
|
||||||
return this.currentState === cpState.REDEEMED;
|
return this.currentState === cpState.REDEEMED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static fromBuffer(buffer) {
|
||||||
* Serialize/deserialize commercial paper
|
return CommercialPaper.deserialize(Buffer.from(JSON.parse(buffer)));
|
||||||
**/
|
}
|
||||||
|
|
||||||
serialize() {
|
toBuffer() {
|
||||||
return Buffer.from(JSON.stringify(this));
|
return Buffer.from(JSON.stringify(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize a state data to commercial paper
|
||||||
|
* @param {Buffer} data to form back into the object
|
||||||
|
*/
|
||||||
static deserialize(data) {
|
static deserialize(data) {
|
||||||
return Object.create(new CommercialPaper, JSON.parse(data));
|
return State.deserializeClass(data, CommercialPaper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory method to create a commercial paper object
|
||||||
|
*/
|
||||||
|
static createInstance(issuer, paperNumber, issueDateTime, maturityDateTime, faceValue) {
|
||||||
|
return new CommercialPaper({ issuer, paperNumber, issueDateTime, maturityDateTime, faceValue });
|
||||||
|
}
|
||||||
|
|
||||||
|
static getClass() {
|
||||||
|
return 'org.papernet.commercialpaper';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = CommercialPaper;
|
||||||
CommercialPaper,
|
|
||||||
};
|
|
||||||
|
|
@ -8,25 +8,25 @@ SPDX-License-Identifier: Apache-2.0
|
||||||
const { Contract, Context } = require('fabric-contract-api');
|
const { Contract, Context } = require('fabric-contract-api');
|
||||||
|
|
||||||
// PaperNet specifc classes
|
// PaperNet specifc classes
|
||||||
const { CommercialPaper } = require('./paper.js');
|
const CommercialPaper = require('./paper.js');
|
||||||
|
const PaperList = require('./paperlist.js');
|
||||||
// Utility classes
|
|
||||||
const { StateList } = require('./ledgerutils.js');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define custom context for commercial paper by extending Fabric Context class
|
* A custom context provides easy access to list of all commercial papers
|
||||||
*/
|
*/
|
||||||
class CommericalPaperContext extends Context {
|
class CommercialPaperContext extends Context {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
// All papers held ins a list of Fabric states
|
super();
|
||||||
this.cpList = new StateList(this, 'org.papernet.commercialpaperlist');
|
// All papers are held in a list of papers
|
||||||
|
this.paperList = new PaperList(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define commercial paper smart contract by extending Fabric Contract class
|
* Define commercial paper smart contract by extending Fabric Contract class
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
class CommercialPaperContract extends Contract {
|
class CommercialPaperContract extends Contract {
|
||||||
|
|
||||||
|
|
@ -35,20 +35,27 @@ class CommercialPaperContract extends Contract {
|
||||||
super('org.papernet.commercialpaper');
|
super('org.papernet.commercialpaper');
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method is called when a smart contract is instantiated
|
/**
|
||||||
// Often used to set up the ledger main transactions are called
|
* Define a custom context for commercial paper
|
||||||
instantiate() {
|
*/
|
||||||
|
createContext() {
|
||||||
|
return new CommercialPaperContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
// A custom context provides easy access to the list of commercial papers
|
/**
|
||||||
createContext() {
|
* Instantiate to perform any setup of the ledger that might be required.
|
||||||
return new CommericalPaperContext();
|
* @param {Context} ctx the transaction context
|
||||||
|
*/
|
||||||
|
async instantiate(ctx) {
|
||||||
|
// No implementation required with this example
|
||||||
|
// It could be where data migration is performed, if necessary
|
||||||
|
console.log('Instantiate the contract');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Issue commercial paper
|
* Issue commercial paper
|
||||||
* @param {TxContext} ctx the transaction context
|
*
|
||||||
|
* @param {Context} ctx the transaction context
|
||||||
* @param {String} issuer commercial paper issuer
|
* @param {String} issuer commercial paper issuer
|
||||||
* @param {Integer} paperNumber paper number for this issuer
|
* @param {Integer} paperNumber paper number for this issuer
|
||||||
* @param {String} issueDateTime paper issue date
|
* @param {String} issueDateTime paper issue date
|
||||||
|
|
@ -57,20 +64,26 @@ class CommercialPaperContract extends Contract {
|
||||||
*/
|
*/
|
||||||
async issue(ctx, issuer, paperNumber, issueDateTime, maturityDateTime, faceValue) {
|
async issue(ctx, issuer, paperNumber, issueDateTime, maturityDateTime, faceValue) {
|
||||||
|
|
||||||
let cp = new CommercialPaper(issuer, paperNumber, issueDateTime, maturityDateTime, faceValue);
|
// create an instance of the paper
|
||||||
|
let paper = CommercialPaper.createInstance(issuer, paperNumber, issueDateTime, maturityDateTime, faceValue);
|
||||||
|
|
||||||
// Smart contract, rather than paper, moves paper into ISSUED state
|
// Smart contract, rather than paper, moves paper into ISSUED state
|
||||||
cp.setIssued();
|
paper.setIssued();
|
||||||
|
|
||||||
|
// Newly issued paper is owned by the issuer
|
||||||
|
paper.setOwner(issuer);
|
||||||
|
|
||||||
// Add the paper to the list of all similar commercial papers in the ledger world state
|
// Add the paper to the list of all similar commercial papers in the ledger world state
|
||||||
await ctx.cpList.addState(cp);
|
await ctx.paperList.addPaper(paper);
|
||||||
|
|
||||||
return cp.serialize();
|
// Must return a serialized paper to caller of smart contract
|
||||||
|
return paper.toBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Buy commercial paper
|
* Buy commercial paper
|
||||||
* @param {TxContext} ctx the transaction context
|
*
|
||||||
|
* @param {Context} ctx the transaction context
|
||||||
* @param {String} issuer commercial paper issuer
|
* @param {String} issuer commercial paper issuer
|
||||||
* @param {Integer} paperNumber paper number for this issuer
|
* @param {Integer} paperNumber paper number for this issuer
|
||||||
* @param {String} currentOwner current owner of paper
|
* @param {String} currentOwner current owner of paper
|
||||||
|
|
@ -80,31 +93,36 @@ class CommercialPaperContract extends Contract {
|
||||||
*/
|
*/
|
||||||
async buy(ctx, issuer, paperNumber, currentOwner, newOwner, price, purchaseDateTime) {
|
async buy(ctx, issuer, paperNumber, currentOwner, newOwner, price, purchaseDateTime) {
|
||||||
|
|
||||||
let cpKey = CommercialPaper.makeKey([issuer, paperNumber]);
|
// Retrieve the current paper using key fields provided
|
||||||
|
let paperKey = CommercialPaper.makeKey([issuer, paperNumber]);
|
||||||
|
let paper = await ctx.paperList.getPaper(paperKey);
|
||||||
|
|
||||||
let cp = await ctx.cpList.getState(cpKey);
|
// Validate current owner
|
||||||
|
if (paper.getOwner() !== currentOwner) {
|
||||||
if (cp.getOwner() !== currentOwner) {
|
|
||||||
throw new Error('Paper ' + issuer + paperNumber + ' is not owned by ' + currentOwner);
|
throw new Error('Paper ' + issuer + paperNumber + ' is not owned by ' + currentOwner);
|
||||||
}
|
}
|
||||||
|
|
||||||
// First buy moves state from ISSUED to TRADING
|
// First buy moves state from ISSUED to TRADING
|
||||||
if (cp.isIssued()) {
|
if (paper.isIssued()) {
|
||||||
cp.setTrading();
|
paper.setTrading();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check paper is not already REDEEMED
|
// Check paper is not already REDEEMED
|
||||||
if (cp.IsTrading()) {
|
if (paper.isTrading()) {
|
||||||
cp.setOwner(newOwner);
|
paper.setOwner(newOwner);
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Paper ' + issuer + paperNumber + ' is not trading. Current state = ' + cp.getCurrentState());
|
throw new Error('Paper ' + issuer + paperNumber + ' is not trading. Current state = ' + cp.getCurrentState());
|
||||||
}
|
}
|
||||||
|
|
||||||
await ctx.cpList.updateState(cp);
|
// Update the paper
|
||||||
return cp.deserialize();
|
await ctx.paperList.updatePaper(paper);
|
||||||
|
return paper.toBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Redeem commercial paper
|
* Redeem commercial paper
|
||||||
* @param {TxContext} ctx the transaction context
|
*
|
||||||
|
* @param {Context} ctx the transaction context
|
||||||
* @param {String} issuer commercial paper issuer
|
* @param {String} issuer commercial paper issuer
|
||||||
* @param {Integer} paperNumber paper number for this issuer
|
* @param {Integer} paperNumber paper number for this issuer
|
||||||
* @param {String} redeemingOwner redeeming owner of paper
|
* @param {String} redeemingOwner redeeming owner of paper
|
||||||
|
|
@ -112,27 +130,27 @@ class CommercialPaperContract extends Contract {
|
||||||
*/
|
*/
|
||||||
async redeem(ctx, issuer, paperNumber, redeemingOwner, redeemDateTime) {
|
async redeem(ctx, issuer, paperNumber, redeemingOwner, redeemDateTime) {
|
||||||
|
|
||||||
let cpKey = CommercialPaper.makeKey([issuer, paperNumber]);
|
let paperKey = CommercialPaper.makeKey([issuer, paperNumber]);
|
||||||
|
|
||||||
let cp = await ctx.cpList.getState(cpKey);
|
let paper = await ctx.paperList.getPaper(paperKey);
|
||||||
|
|
||||||
// Check paper is TRADING, not REDEEMED
|
// Check paper is not REDEEMED
|
||||||
if (cp.IsRedeemed()) {
|
if (paper.isRedeemed()) {
|
||||||
throw new Error('Paper ' + issuer + paperNumber + ' already redeemed');
|
throw new Error('Paper ' + issuer + paperNumber + ' already redeemed');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that the redeemer owns the commercial paper before redeeming it
|
// Verify that the redeemer owns the commercial paper before redeeming it
|
||||||
if (cp.getOwner() === redeemingOwner) {
|
if (paper.getOwner() === redeemingOwner) {
|
||||||
cp.setOwner(cp.getIssuer());
|
paper.setOwner(paper.getIssuer());
|
||||||
cp.setRedeemed();
|
paper.setRedeemed();
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Redeeming owner does not own paper' + issuer + paperNumber);
|
throw new Error('Redeeming owner does not own paper' + issuer + paperNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
await ctx.cpList.updateState(cp);
|
await ctx.paperList.updatePaper(paper);
|
||||||
return cp.serialize();
|
return paper.toBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = CommericalPaperContract;
|
module.exports = CommercialPaperContract;
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Utility class for collections of ledger states -- a state list
|
||||||
|
const StateList = require('./../ledger-api/statelist.js');
|
||||||
|
|
||||||
|
const CommercialPaper = require('./paper.js');
|
||||||
|
|
||||||
|
class PaperList extends StateList {
|
||||||
|
|
||||||
|
constructor(ctx) {
|
||||||
|
super(ctx, 'org.papernet.commercialpaperlist');
|
||||||
|
this.use(CommercialPaper);
|
||||||
|
}
|
||||||
|
|
||||||
|
async addPaper(paper) {
|
||||||
|
return this.addState(paper);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPaper(paperKey) {
|
||||||
|
return this.getState(paperKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
async updatePaper(paper) {
|
||||||
|
return this.updateState(paper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = PaperList;
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
{
|
{
|
||||||
"name": "smart-contract",
|
"name": "papernet-js",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"description": "Smart Contract",
|
"description": "Papernet Contract",
|
||||||
|
"main": "index.js",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8",
|
"node": ">=8",
|
||||||
"npm": ">=5"
|
"npm": ">=5"
|
||||||
|
|
@ -9,22 +10,25 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"pretest": "npm run lint",
|
"pretest": "npm run lint",
|
||||||
"test": "nyc mocha --recursive",
|
"test": "nyc mocha test --recursive",
|
||||||
"start": "startChaincode"
|
"start": "fabric-chaincode-node start",
|
||||||
|
"mocha": "mocha test --recursive"
|
||||||
},
|
},
|
||||||
"engineStrict": true,
|
"engineStrict": true,
|
||||||
"author": "Anthony ODowd",
|
"author": "hyperledger",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fabric-shim": "unstable",
|
"fabric-contract-api": "^1.4.0-snapshot.17",
|
||||||
"fabric-contract-api": "unstable"
|
"fabric-shim": "^1.4.0-snapshot.27"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"chai": "^4.1.2",
|
"chai": "^4.1.2",
|
||||||
|
"chai-as-promised": "^7.1.1",
|
||||||
"eslint": "^4.19.1",
|
"eslint": "^4.19.1",
|
||||||
"mocha": "^5.2.0",
|
"mocha": "^5.2.0",
|
||||||
"nyc": "^12.0.2",
|
"nyc": "^12.0.2",
|
||||||
"sinon": "^6.0.0"
|
"sinon": "^6.0.0",
|
||||||
|
"sinon-chai": "^3.2.0"
|
||||||
},
|
},
|
||||||
"nyc": {
|
"nyc": {
|
||||||
"exclude": [
|
"exclude": [
|
||||||
|
|
@ -0,0 +1,129 @@
|
||||||
|
---
|
||||||
|
#
|
||||||
|
# The network connection profile provides client applications the information about the target
|
||||||
|
# blockchain network that are necessary for the applications to interact with it. These are all
|
||||||
|
# knowledge that must be acquired from out-of-band sources. This file provides such a source.
|
||||||
|
#
|
||||||
|
name: "basic-network"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Any properties with an "x-" prefix will be treated as application-specific, exactly like how naming
|
||||||
|
# in HTTP headers or swagger properties work. The SDK will simply ignore these fields and leave
|
||||||
|
# them for the applications to process. This is a mechanism for different components of an application
|
||||||
|
# to exchange information that are not part of the standard schema described below. In particular,
|
||||||
|
# the "x-type" property with the "hlfv1" value example below is used by Hyperledger Composer to
|
||||||
|
# determine the type of Fabric networks (v0.6 vs. v1.0) it needs to work with.
|
||||||
|
#
|
||||||
|
x-type: "hlfv1"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Describe what the target network is/does.
|
||||||
|
#
|
||||||
|
description: "The basic network"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Schema version of the content. Used by the SDK to apply the corresponding parsing rules.
|
||||||
|
#
|
||||||
|
version: "1.0"
|
||||||
|
|
||||||
|
#
|
||||||
|
# [Optional]. But most apps would have this section so that channel objects can be constructed
|
||||||
|
# based on the content below. If an app is creating channels, then it likely will not need this
|
||||||
|
# section.
|
||||||
|
#
|
||||||
|
channels:
|
||||||
|
# name of the channel
|
||||||
|
mychannel:
|
||||||
|
# Required. list of orderers designated by the application to use for transactions on this
|
||||||
|
# channel. This list can be a result of access control ("org1" can only access "ordererA"), or
|
||||||
|
# operational decisions to share loads from applications among the orderers. The values must
|
||||||
|
# be "names" of orgs defined under "organizations/peers"
|
||||||
|
orderers:
|
||||||
|
- orderer.example.com
|
||||||
|
|
||||||
|
# Required. list of peers from participating orgs
|
||||||
|
peers:
|
||||||
|
peer0.org1.example.com:
|
||||||
|
# [Optional]. will this peer be sent transaction proposals for endorsement? The peer must
|
||||||
|
# have the chaincode installed. The app can also use this property to decide which peers
|
||||||
|
# to send the chaincode install request. Default: true
|
||||||
|
endorsingPeer: true
|
||||||
|
|
||||||
|
# [Optional]. will this peer be sent query proposals? The peer must have the chaincode
|
||||||
|
# installed. The app can also use this property to decide which peers to send the
|
||||||
|
# chaincode install request. Default: true
|
||||||
|
chaincodeQuery: true
|
||||||
|
|
||||||
|
# [Optional]. will this peer be sent query proposals that do not require chaincodes, like
|
||||||
|
# queryBlock(), queryTransaction(), etc. Default: true
|
||||||
|
ledgerQuery: true
|
||||||
|
|
||||||
|
# [Optional]. will this peer be the target of the SDK's listener registration? All peers can
|
||||||
|
# produce events but the app typically only needs to connect to one to listen to events.
|
||||||
|
# Default: true
|
||||||
|
eventSource: true
|
||||||
|
|
||||||
|
#
|
||||||
|
# list of participating organizations in this network
|
||||||
|
#
|
||||||
|
organizations:
|
||||||
|
Org1:
|
||||||
|
mspid: Org1MSP
|
||||||
|
|
||||||
|
peers:
|
||||||
|
- peer0.org1.example.com
|
||||||
|
|
||||||
|
# [Optional]. Certificate Authorities issue certificates for identification purposes in a Fabric based
|
||||||
|
# network. Typically certificates provisioning is done in a separate process outside of the
|
||||||
|
# runtime network. Fabric-CA is a special certificate authority that provides a REST APIs for
|
||||||
|
# dynamic certificate management (enroll, revoke, re-enroll). The following section is only for
|
||||||
|
# Fabric-CA servers.
|
||||||
|
certificateAuthorities:
|
||||||
|
- ca-org1
|
||||||
|
|
||||||
|
#
|
||||||
|
# List of orderers to send transaction and channel create/update requests to. For the time
|
||||||
|
# being only one orderer is needed. If more than one is defined, which one get used by the
|
||||||
|
# SDK is implementation specific. Consult each SDK's documentation for its handling of orderers.
|
||||||
|
#
|
||||||
|
orderers:
|
||||||
|
orderer.example.com:
|
||||||
|
url: grpc://localhost:7050
|
||||||
|
|
||||||
|
# these are standard properties defined by the gRPC library
|
||||||
|
# they will be passed in as-is to gRPC client constructor
|
||||||
|
grpcOptions:
|
||||||
|
ssl-target-name-override: orderer.example.com
|
||||||
|
|
||||||
|
#
|
||||||
|
# List of peers to send various requests to, including endorsement, query
|
||||||
|
# and event listener registration.
|
||||||
|
#
|
||||||
|
peers:
|
||||||
|
peer0.org1.example.com:
|
||||||
|
# this URL is used to send endorsement and query requests
|
||||||
|
url: grpc://localhost:7051
|
||||||
|
|
||||||
|
grpcOptions:
|
||||||
|
ssl-target-name-override: peer0.org1.example.com
|
||||||
|
request-timeout: 120001
|
||||||
|
|
||||||
|
# Fabric-CA is a special kind of Certificate Authority provided by Hyperledger Fabric which allows
|
||||||
|
# certificate management to be done via REST APIs. Application may choose to use a standard
|
||||||
|
# Certificate Authority instead of Fabric-CA, in which case this section would not be specified.
|
||||||
|
#
|
||||||
|
certificateAuthorities:
|
||||||
|
ca-org1:
|
||||||
|
url: http://localhost:7054
|
||||||
|
# the properties specified under this object are passed to the 'http' client verbatim when
|
||||||
|
# making the request to the Fabric-CA server
|
||||||
|
httpOptions:
|
||||||
|
verify: false
|
||||||
|
|
||||||
|
# Fabric-CA supports dynamic user enrollment via REST APIs. A "root" user, a.k.a registrar, is
|
||||||
|
# needed to enroll and invoke new users.
|
||||||
|
registrar:
|
||||||
|
- enrollId: admin
|
||||||
|
enrollSecret: adminpw
|
||||||
|
# [Optional] The optional name of the CA.
|
||||||
|
caName: ca-org1
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
# blockchain network that are necessary for the applications to interact with it. These are all
|
# blockchain network that are necessary for the applications to interact with it. These are all
|
||||||
# knowledge that must be acquired from out-of-band sources. This file provides such a source.
|
# knowledge that must be acquired from out-of-band sources. This file provides such a source.
|
||||||
#
|
#
|
||||||
name: "global-trade-network"
|
name: "finance-networks"
|
||||||
|
|
||||||
#
|
#
|
||||||
# Any properties with an "x-" prefix will be treated as application-specific, exactly like how naming
|
# Any properties with an "x-" prefix will be treated as application-specific, exactly like how naming
|
||||||
|
|
@ -19,7 +19,7 @@ x-type: "hlfv1"
|
||||||
#
|
#
|
||||||
# Describe what the target network is/does.
|
# Describe what the target network is/does.
|
||||||
#
|
#
|
||||||
description: "The network to be in if you want to stay in the global trade business"
|
description: "A gateway connection file for the PaperNet networks"
|
||||||
|
|
||||||
#
|
#
|
||||||
# Schema version of the content. Used by the SDK to apply the corresponding parsing rules.
|
# Schema version of the content. Used by the SDK to apply the corresponding parsing rules.
|
||||||
|
|
@ -59,13 +59,13 @@ version: "1.0"
|
||||||
#
|
#
|
||||||
channels:
|
channels:
|
||||||
# name of the channel
|
# name of the channel
|
||||||
PaperNet:
|
papernet:
|
||||||
# Required. list of orderers designated by the application to use for transactions on this
|
# Required. list of orderers designated by the application to use for transactions on this
|
||||||
# channel. This list can be a result of access control ("org1" can only access "ordererA"), or
|
# channel. This list can be a result of access control ("org1" can only access "ordererA"), or
|
||||||
# operational decisions to share loads from applications among the orderers. The values must
|
# operational decisions to share loads from applications among the orderers. The values must
|
||||||
# be "names" of orgs defined under "organizations/peers"
|
# be "names" of orgs defined under "organizations/peers"
|
||||||
orderers:
|
orderers:
|
||||||
- orderer.example.com
|
- orderer.magnetocorp.com
|
||||||
|
|
||||||
# Required. list of peers from participating orgs
|
# Required. list of peers from participating orgs
|
||||||
peers:
|
peers:
|
||||||
|
|
@ -119,7 +119,7 @@ organizations:
|
||||||
# dynamic certificate management (enroll, revoke, re-enroll). The following section is only for
|
# dynamic certificate management (enroll, revoke, re-enroll). The following section is only for
|
||||||
# Fabric-CA servers.
|
# Fabric-CA servers.
|
||||||
certificateAuthorities:
|
certificateAuthorities:
|
||||||
- ca-org1
|
- ca-magnetocorp
|
||||||
|
|
||||||
# [Optional]. If the application is going to make requests that are reserved to organization
|
# [Optional]. If the application is going to make requests that are reserved to organization
|
||||||
# administrators, including creating/updating channels, installing/instantiating chaincodes, it
|
# administrators, including creating/updating channels, installing/instantiating chaincodes, it
|
||||||
|
|
@ -129,9 +129,9 @@ organizations:
|
||||||
# this way. The SDK should allow applications to set the org admin identity via APIs, and only use
|
# this way. The SDK should allow applications to set the org admin identity via APIs, and only use
|
||||||
# this route as an alternative when it exists.
|
# this route as an alternative when it exists.
|
||||||
adminPrivateKey:
|
adminPrivateKey:
|
||||||
path: test/fixtures/channel/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/keystore/9022d671ceedbb24af3ea69b5a8136cc64203df6b9920e26f48123fcfcb1d2e9_sk
|
path: commercial-paper/organization/magnetocorp/users/Admin@magnetocorp/keystore/9022d671ceedbb24af3ea69b5a8136cc64203df6b9920e26f48123fcfcb1d2e9_sk
|
||||||
signedCert:
|
signedCert:
|
||||||
path: test/fixtures/channel/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/signcerts/Admin@org1.example.com-cert.pem
|
path: comercial-paper/organization/magnetocorp/users/Admin@magnetocorp.com/signcerts/Admin@magnetocorp.com-cert.pem
|
||||||
|
|
||||||
# the profile will contain public information about organizations other than the one it belongs to.
|
# the profile will contain public information about organizations other than the one it belongs to.
|
||||||
# These are necessary information to make transaction lifecycles work, including MSP IDs and
|
# These are necessary information to make transaction lifecycles work, including MSP IDs and
|
||||||
|
|
@ -143,11 +143,11 @@ organizations:
|
||||||
peers:
|
peers:
|
||||||
- peer1.digibank.com
|
- peer1.digibank.com
|
||||||
certificateAuthorities:
|
certificateAuthorities:
|
||||||
- ca-org2
|
- ca-digibank
|
||||||
adminPrivateKey:
|
adminPrivateKey:
|
||||||
path: test/fixtures/channel/crypto-config/peerOrganizations/org2.example.com/users/Admin@org2.example.com/keystore/5a983ddcbefe52a7f9b8ee5b85a590c3e3a43c4ccd70c7795bec504e7f74848d_sk
|
path: commercial-paper/organization/digibank/users/Admin@digibank.com/keystore/5a983ddcbefe52a7f9b8ee5b85a590c3e3a43c4ccd70c7795bec504e7f74848d_sk
|
||||||
signedCert:
|
signedCert:
|
||||||
path: test/fixtures/channel/crypto-config/peerOrganizations/org2.example.com/users/Admin@org2.example.com/signcerts/Admin@org2.example.com-cert.pem
|
path: commercial-paper/organization/digibank/users/Admin@digibank.com/signcerts/Admin@digibank.com-cert.pem
|
||||||
|
|
||||||
#
|
#
|
||||||
# List of orderers to send transaction and channel create/update requests to. For the time
|
# List of orderers to send transaction and channel create/update requests to. For the time
|
||||||
|
|
@ -155,7 +155,7 @@ organizations:
|
||||||
# SDK is implementation specific. Consult each SDK's documentation for its handling of orderers.
|
# SDK is implementation specific. Consult each SDK's documentation for its handling of orderers.
|
||||||
#
|
#
|
||||||
orderers:
|
orderers:
|
||||||
orderer.example.com:
|
orderer.magnetocorp.com:
|
||||||
url: grpcs://localhost:7050
|
url: grpcs://localhost:7050
|
||||||
|
|
||||||
# these are standard properties defined by the gRPC library
|
# these are standard properties defined by the gRPC library
|
||||||
|
|
@ -164,7 +164,7 @@ orderers:
|
||||||
ssl-target-name-override: orderer.example.com
|
ssl-target-name-override: orderer.example.com
|
||||||
|
|
||||||
tlsCACerts:
|
tlsCACerts:
|
||||||
path: test/fixtures/channel/crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/tlscacerts/example.com-cert.pem
|
path: comercial-paper/organization/magnetocorp/orderer/orderer.magnetocorp.com/tlscacerts/example.com-cert.pem
|
||||||
|
|
||||||
#
|
#
|
||||||
# List of peers to send various requests to, including endorsement, query
|
# List of peers to send various requests to, including endorsement, query
|
||||||
|
|
@ -202,7 +202,7 @@ certificateAuthorities:
|
||||||
httpOptions:
|
httpOptions:
|
||||||
verify: false
|
verify: false
|
||||||
tlsCACerts:
|
tlsCACerts:
|
||||||
path: test/fixtures/channel/crypto-config/peerOrganizations/org1.example.com/ca/org1.example.com-cert.pem
|
path: commercial-paper/organization/magnetocorp/ca/magnetocorp.com-cert.pem
|
||||||
|
|
||||||
# Fabric-CA supports dynamic user enrollment via REST APIs. A "root" user, a.k.a registrar, is
|
# Fabric-CA supports dynamic user enrollment via REST APIs. A "root" user, a.k.a registrar, is
|
||||||
# needed to enroll and invoke new users.
|
# needed to enroll and invoke new users.
|
||||||
|
|
@ -210,16 +210,16 @@ certificateAuthorities:
|
||||||
- enrollId: admin
|
- enrollId: admin
|
||||||
enrollSecret: adminpw
|
enrollSecret: adminpw
|
||||||
# [Optional] The optional name of the CA.
|
# [Optional] The optional name of the CA.
|
||||||
caName: ca-org1
|
caName: ca-magnetocorp
|
||||||
|
|
||||||
ca-org2:
|
ca-org2:
|
||||||
url: https://localhost:8054
|
url: https://localhost:8054
|
||||||
httpOptions:
|
httpOptions:
|
||||||
verify: false
|
verify: false
|
||||||
tlsCACerts:
|
tlsCACerts:
|
||||||
path: test/fixtures/channel/crypto-config/peerOrganizations/org2.example.com/ca/org2.example.com-cert.pem
|
path: commercial-paper/organization/digibank/ca/digibank.com-cert.pem
|
||||||
registrar:
|
registrar:
|
||||||
- enrollId: admin
|
- enrollId: admin
|
||||||
enrollSecret: adminpw
|
enrollSecret: adminpw
|
||||||
# [Optional] The optional name of the CA.
|
# [Optional] The optional name of the CA.
|
||||||
caName: ca-org2
|
caName: ca-digibank
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
env: {
|
||||||
|
node: true,
|
||||||
|
mocha: true
|
||||||
|
},
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 8,
|
||||||
|
sourceType: 'script'
|
||||||
|
},
|
||||||
|
extends: "eslint:recommended",
|
||||||
|
rules: {
|
||||||
|
indent: ['error', 4],
|
||||||
|
'linebreak-style': ['error', 'unix'],
|
||||||
|
quotes: ['error', 'single'],
|
||||||
|
semi: ['error', 'always'],
|
||||||
|
'no-unused-vars': ['error', { args: 'none' }],
|
||||||
|
'no-console': 'off',
|
||||||
|
curly: 'error',
|
||||||
|
eqeqeq: 'error',
|
||||||
|
'no-throw-literal': 'error',
|
||||||
|
strict: 'error',
|
||||||
|
'no-var': 'error',
|
||||||
|
'dot-notation': 'error',
|
||||||
|
'no-tabs': 'error',
|
||||||
|
'no-trailing-spaces': 'error',
|
||||||
|
'no-use-before-define': 'error',
|
||||||
|
'no-useless-call': 'error',
|
||||||
|
'no-with': 'error',
|
||||||
|
'operator-linebreak': 'error',
|
||||||
|
yoda: 'error',
|
||||||
|
'quote-props': ['error', 'as-needed']
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Bring key classes into scope, most importantly Fabric SDK network class
|
||||||
|
const fs = require('fs');
|
||||||
|
const { FileSystemWallet, X509WalletMixin } = require('fabric-network');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const fixtures = path.resolve(__dirname, '../../../../basic-network');
|
||||||
|
|
||||||
|
// A wallet stores a collection of identities
|
||||||
|
const wallet = new FileSystemWallet('../identity/user/isabella/wallet');
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
|
||||||
|
// Main try/catch block
|
||||||
|
try {
|
||||||
|
|
||||||
|
// Identity to credentials to be stored in the wallet
|
||||||
|
const credPath = path.join(fixtures, '/crypto-config/peerOrganizations/org1.example.com/users/User1@org1.example.com');
|
||||||
|
const cert = fs.readFileSync(path.join(credPath, '/msp/signcerts/User1@org1.example.com-cert.pem')).toString();
|
||||||
|
const key = fs.readFileSync(path.join(credPath, '/msp/keystore/c75bd6911aca808941c3557ee7c97e90f3952e379497dc55eb903f31b50abc83_sk')).toString();
|
||||||
|
|
||||||
|
// Load credentials into wallet
|
||||||
|
const identityLabel = 'User1@org1.example.com';
|
||||||
|
const identity = X509WalletMixin.createIdentity('Org1MSP', cert, key);
|
||||||
|
|
||||||
|
await wallet.import(identityLabel, identity);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.log(`Error adding to wallet. ${error}`);
|
||||||
|
console.log(error.stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main().then(() => {
|
||||||
|
console.log('done');
|
||||||
|
}).catch((e) => {
|
||||||
|
console.log(e);
|
||||||
|
console.log(e.stack);
|
||||||
|
process.exit(-1);
|
||||||
|
});
|
||||||
102
commercial-paper/organization/magnetocorp/application/issue.js
Normal file
102
commercial-paper/organization/magnetocorp/application/issue.js
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This application has 6 basic steps:
|
||||||
|
* 1. Select an identity from a wallet
|
||||||
|
* 2. Connect to network gateway
|
||||||
|
* 3. Access PaperNet network
|
||||||
|
* 4. Construct request to issue commercial paper
|
||||||
|
* 5. Submit transaction
|
||||||
|
* 6. Process response
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Bring key classes into scope, most importantly Fabric SDK network class
|
||||||
|
const fs = require('fs');
|
||||||
|
const yaml = require('js-yaml');
|
||||||
|
const { FileSystemWallet, Gateway } = require('fabric-network');
|
||||||
|
const CommercialPaper = require('../contract/lib/paper.js');
|
||||||
|
|
||||||
|
// A wallet stores a collection of identities for use
|
||||||
|
//const wallet = new FileSystemWallet('../user/isabella/wallet');
|
||||||
|
const wallet = new FileSystemWallet('../identity/user/isabella/wallet');
|
||||||
|
|
||||||
|
// Main program function
|
||||||
|
async function main() {
|
||||||
|
|
||||||
|
// A gateway defines the peers used to access Fabric networks
|
||||||
|
const gateway = new Gateway();
|
||||||
|
|
||||||
|
// Main try/catch block
|
||||||
|
try {
|
||||||
|
|
||||||
|
// Specify userName for network access
|
||||||
|
// const userName = 'isabella.issuer@magnetocorp.com';
|
||||||
|
const userName = 'User1@org1.example.com';
|
||||||
|
|
||||||
|
// Load connection profile; will be used to locate a gateway
|
||||||
|
let connectionProfile = yaml.safeLoad(fs.readFileSync('../gateway/networkConnection.yaml', 'utf8'));
|
||||||
|
|
||||||
|
// Set connection options; identity and wallet
|
||||||
|
let connectionOptions = {
|
||||||
|
identity: userName,
|
||||||
|
wallet: wallet,
|
||||||
|
discovery: { enabled:false, asLocalhost: true }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Connect to gateway using application specified parameters
|
||||||
|
console.log('Connect to Fabric gateway.');
|
||||||
|
|
||||||
|
await gateway.connect(connectionProfile, connectionOptions);
|
||||||
|
|
||||||
|
// Access PaperNet network
|
||||||
|
console.log('Use network channel: mychannel.');
|
||||||
|
|
||||||
|
const network = await gateway.getNetwork('mychannel');
|
||||||
|
|
||||||
|
// Get addressability to commercial paper contract
|
||||||
|
console.log('Use org.papernet.commercialpaper smart contract.');
|
||||||
|
|
||||||
|
const contract = await network.getContract('papercontract', 'org.papernet.commercialpaper');
|
||||||
|
|
||||||
|
// issue commercial paper
|
||||||
|
console.log('Submit commercial paper issue transaction.');
|
||||||
|
|
||||||
|
const issueResponse = await contract.submitTransaction('issue', 'MagnetoCorp', '00001', '2020-05-31', '2020-11-30', '5000000');
|
||||||
|
|
||||||
|
// process response
|
||||||
|
console.log('Process issue transaction response.');
|
||||||
|
|
||||||
|
let paper = CommercialPaper.fromBuffer(issueResponse);
|
||||||
|
|
||||||
|
console.log(`${paper.issuer} commercial paper : ${paper.paperNumber} successfully issued for value ${paper.faceValue}`);
|
||||||
|
console.log('Transaction complete.');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
|
||||||
|
console.log(`Error processing transaction. ${error}`);
|
||||||
|
console.log(error.stack);
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
|
||||||
|
// Disconnect from the gateway
|
||||||
|
console.log('Disconnect from Fabric gateway.')
|
||||||
|
gateway.disconnect();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
main().then(() => {
|
||||||
|
|
||||||
|
console.log('Issue program complete.');
|
||||||
|
|
||||||
|
}).catch((e) => {
|
||||||
|
|
||||||
|
console.log('Issue program exception.');
|
||||||
|
console.log(e);
|
||||||
|
console.log(e.stack);
|
||||||
|
process.exit(-1);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"name": "nodejs",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "issue.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "rm -rf _idwallet && node addToWallet.js && node issue.js"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"fabric-network": "^1.4.0-beta",
|
||||||
|
"fabric-client": "^1.4.0-beta",
|
||||||
|
"js-yaml": "^3.12.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"eslint": "^5.6.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
#
|
||||||
|
# Copyright IBM Corp All Rights Reserved
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
version: '2'
|
||||||
|
|
||||||
|
networks:
|
||||||
|
basic:
|
||||||
|
external:
|
||||||
|
name: net_basic
|
||||||
|
|
||||||
|
services:
|
||||||
|
cliMagnetoCorp:
|
||||||
|
container_name: cliMagnetoCorp
|
||||||
|
image: hyperledger/fabric-tools
|
||||||
|
tty: true
|
||||||
|
environment:
|
||||||
|
- GOPATH=/opt/gopath
|
||||||
|
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
|
||||||
|
- CORE_LOGGING_LEVEL=info
|
||||||
|
- CORE_PEER_ID=cli
|
||||||
|
- CORE_PEER_ADDRESS=peer0.org1.example.com:7051
|
||||||
|
- CORE_PEER_LOCALMSPID=Org1MSP
|
||||||
|
- CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
|
||||||
|
- CORE_CHAINCODE_KEEPALIVE=10
|
||||||
|
working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
|
||||||
|
command: /bin/bash
|
||||||
|
volumes:
|
||||||
|
- /var/run/:/host/var/run/
|
||||||
|
- ./../../../../organization/magnetocorp:/opt/gopath/src/github.com/
|
||||||
|
- ./../../../../../basic-network/crypto-config:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/
|
||||||
|
networks:
|
||||||
|
- basic
|
||||||
|
#depends_on:
|
||||||
|
# - orderer.example.com
|
||||||
|
# - peer0.org1.example.com
|
||||||
|
# - couchdb
|
||||||
25
commercial-paper/organization/magnetocorp/configuration/cli/monitordocker.sh
Executable file
25
commercial-paper/organization/magnetocorp/configuration/cli/monitordocker.sh
Executable file
|
|
@ -0,0 +1,25 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# This script uses the logspout and http stream tools to let you watch the docker containers
|
||||||
|
# in action.
|
||||||
|
#
|
||||||
|
# More information at https://github.com/gliderlabs/logspout/tree/master/httpstream
|
||||||
|
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
DOCKER_NETWORK=basicnetwork_basic
|
||||||
|
else
|
||||||
|
DOCKER_NETWORK="$1"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo Starting monitoring on all containers on the network ${DOCKER_NETWORK}
|
||||||
|
|
||||||
|
docker kill logspout 2> /dev/null 1>&2 || true
|
||||||
|
docker rm logspout 2> /dev/null 1>&2 || true
|
||||||
|
|
||||||
|
docker run -d --name="logspout" \
|
||||||
|
--volume=/var/run/docker.sock:/var/run/docker.sock \
|
||||||
|
--publish=127.0.0.1:8000:80 \
|
||||||
|
--network ${DOCKER_NETWORK} \
|
||||||
|
gliderlabs/logspout
|
||||||
|
sleep 3
|
||||||
|
curl http://127.0.0.1:8000/logs
|
||||||
16
commercial-paper/organization/magnetocorp/contract/.editorconfig
Executable file
16
commercial-paper/organization/magnetocorp/contract/.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
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
|
coverage
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
env: {
|
||||||
|
node: true,
|
||||||
|
mocha: true
|
||||||
|
},
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 8,
|
||||||
|
sourceType: 'script'
|
||||||
|
},
|
||||||
|
extends: "eslint:recommended",
|
||||||
|
rules: {
|
||||||
|
indent: ['error', 4],
|
||||||
|
'linebreak-style': ['error', 'unix'],
|
||||||
|
quotes: ['error', 'single'],
|
||||||
|
semi: ['error', 'always'],
|
||||||
|
'no-unused-vars': ['error', { args: 'none' }],
|
||||||
|
'no-console': 'off',
|
||||||
|
curly: 'error',
|
||||||
|
eqeqeq: 'error',
|
||||||
|
'no-throw-literal': 'error',
|
||||||
|
strict: 'error',
|
||||||
|
'no-var': 'error',
|
||||||
|
'dot-notation': 'error',
|
||||||
|
'no-tabs': 'error',
|
||||||
|
'no-trailing-spaces': 'error',
|
||||||
|
'no-use-before-define': 'error',
|
||||||
|
'no-useless-call': 'error',
|
||||||
|
'no-with': 'error',
|
||||||
|
'operator-linebreak': 'error',
|
||||||
|
yoda: 'error',
|
||||||
|
'quote-props': ['error', 'as-needed']
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
/*
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const cpcontract = require('./lib/papercontract.js');
|
||||||
|
module.exports.contracts = [cpcontract];
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State class. States have a class, unique key, and a lifecycle current state
|
||||||
|
* the current state is determined by the specific subclass
|
||||||
|
*/
|
||||||
|
class State {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {String|Object} class An indentifiable class of the instance
|
||||||
|
* @param {keyParts[]} elements to pull together to make a key for the objects
|
||||||
|
*/
|
||||||
|
constructor(stateClass, keyParts) {
|
||||||
|
this.class = stateClass;
|
||||||
|
this.key = State.makeKey(keyParts);
|
||||||
|
this.currentState = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getClass() {
|
||||||
|
return this.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
getKey() {
|
||||||
|
return this.key;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSplitKey(){
|
||||||
|
return State.splitKey(this.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentState(){
|
||||||
|
return this.currentState;
|
||||||
|
}
|
||||||
|
|
||||||
|
serialize() {
|
||||||
|
return State.serialize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert object to buffer containing JSON data serialization
|
||||||
|
* Typically used before putState()ledger API
|
||||||
|
* @param {Object} JSON object to serialize
|
||||||
|
* @return {buffer} buffer with the data to store
|
||||||
|
*/
|
||||||
|
static serialize(object) {
|
||||||
|
return Buffer.from(JSON.stringify(object));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize object into one of a set of supported JSON classes
|
||||||
|
* i.e. Covert serialized data to JSON object
|
||||||
|
* Typically used after getState() ledger API
|
||||||
|
* @param {data} data to deserialize into JSON object
|
||||||
|
* @param (supportedClasses) the set of classes data can be serialized to
|
||||||
|
* @return {json} json with the data to store
|
||||||
|
*/
|
||||||
|
static deserialize(data, supportedClasses) {
|
||||||
|
let json = JSON.parse(data.toString());
|
||||||
|
let objClass = supportedClasses[json.class];
|
||||||
|
if (!objClass) {
|
||||||
|
throw new Error(`Unknown class of ${json.class}`);
|
||||||
|
}
|
||||||
|
let object = new (objClass)(json);
|
||||||
|
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize object into specific object class
|
||||||
|
* Typically used after getState() ledger API
|
||||||
|
* @param {data} data to deserialize into JSON object
|
||||||
|
* @return {json} json with the data to store
|
||||||
|
*/
|
||||||
|
static deserializeClass(data, objClass) {
|
||||||
|
let json = JSON.parse(data.toString());
|
||||||
|
let object = new (objClass)(json);
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Join the keyParts to make a unififed string
|
||||||
|
* @param (String[]) keyParts
|
||||||
|
*/
|
||||||
|
static makeKey(keyParts) {
|
||||||
|
return keyParts.map(part => JSON.stringify(part)).join(':');
|
||||||
|
}
|
||||||
|
|
||||||
|
static splitKey(key){
|
||||||
|
return key.split(':');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = State;
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
const State = require('./state.js');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* StateList provides a named virtual container for a set of ledger states.
|
||||||
|
* Each state has a unique key which associates it with the container, rather
|
||||||
|
* than the container containing a link to the state. This minimizes collisions
|
||||||
|
* for parallel transactions on different states.
|
||||||
|
*/
|
||||||
|
class StateList {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store Fabric context for subsequent API access, and name of list
|
||||||
|
*/
|
||||||
|
constructor(ctx, listName) {
|
||||||
|
this.ctx = ctx;
|
||||||
|
this.name = listName;
|
||||||
|
this.supportedClasses = {};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a state to the list. Creates a new state in worldstate with
|
||||||
|
* appropriate composite key. Note that state defines its own key.
|
||||||
|
* State object is serialized before writing.
|
||||||
|
*/
|
||||||
|
async addState(state) {
|
||||||
|
let key = this.ctx.stub.createCompositeKey(this.name, state.getSplitKey());
|
||||||
|
let data = State.serialize(state);
|
||||||
|
await this.ctx.stub.putState(key, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a state from the list using supplied keys. Form composite
|
||||||
|
* keys to retrieve state from world state. State data is deserialized
|
||||||
|
* into JSON object before being returned.
|
||||||
|
*/
|
||||||
|
async getState(key) {
|
||||||
|
let ledgerKey = this.ctx.stub.createCompositeKey(this.name, State.splitKey(key));
|
||||||
|
let data = await this.ctx.stub.getState(ledgerKey);
|
||||||
|
let state = State.deserialize(data, this.supportedClasses);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a state in the list. Puts the new state in world state with
|
||||||
|
* appropriate composite key. Note that state defines its own key.
|
||||||
|
* A state is serialized before writing. Logic is very similar to
|
||||||
|
* addState() but kept separate becuase it is semantically distinct.
|
||||||
|
*/
|
||||||
|
async updateState(state) {
|
||||||
|
let key = this.ctx.stub.createCompositeKey(this.name, state.getSplitKey());
|
||||||
|
let data = State.serialize(state);
|
||||||
|
await this.ctx.stub.putState(key, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Stores the class for future deserialization */
|
||||||
|
use(stateClass) {
|
||||||
|
this.supportedClasses[stateClass.getClass()] = stateClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = StateList;
|
||||||
102
commercial-paper/organization/magnetocorp/contract/lib/paper.js
Normal file
102
commercial-paper/organization/magnetocorp/contract/lib/paper.js
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Utility class for ledger state
|
||||||
|
const State = require('./../ledger-api/state.js');
|
||||||
|
|
||||||
|
// Enumerate commercial paper state values
|
||||||
|
const cpState = {
|
||||||
|
ISSUED: 1,
|
||||||
|
TRADING: 2,
|
||||||
|
REDEEMED: 3
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CommercialPaper class extends State class
|
||||||
|
* Class will be used by application and smart contract to define a paper
|
||||||
|
*/
|
||||||
|
class CommercialPaper extends State {
|
||||||
|
|
||||||
|
constructor(obj) {
|
||||||
|
super(CommercialPaper.getClass(), [obj.issuer, obj.paperNumber]);
|
||||||
|
Object.assign(this, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic getters and setters
|
||||||
|
*/
|
||||||
|
getIssuer() {
|
||||||
|
return this.issuer;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIssuer(newIssuer) {
|
||||||
|
this.issuer = newIssuer;
|
||||||
|
}
|
||||||
|
|
||||||
|
getOwner() {
|
||||||
|
return this.owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
setOwner(newOwner) {
|
||||||
|
this.owner = newOwner;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Useful methods to encapsulate commercial paper states
|
||||||
|
*/
|
||||||
|
setIssued() {
|
||||||
|
this.currentState = cpState.ISSUED;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTrading() {
|
||||||
|
this.currentState = cpState.TRADING;
|
||||||
|
}
|
||||||
|
|
||||||
|
setRedeemed() {
|
||||||
|
this.currentState = cpState.REDEEMED;
|
||||||
|
}
|
||||||
|
|
||||||
|
isIssued() {
|
||||||
|
return this.currentState === cpState.ISSUED;
|
||||||
|
}
|
||||||
|
|
||||||
|
isTrading() {
|
||||||
|
return this.currentState === cpState.TRADING;
|
||||||
|
}
|
||||||
|
|
||||||
|
isRedeemed() {
|
||||||
|
return this.currentState === cpState.REDEEMED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromBuffer(buffer) {
|
||||||
|
return CommercialPaper.deserialize(Buffer.from(JSON.parse(buffer)));
|
||||||
|
}
|
||||||
|
|
||||||
|
toBuffer() {
|
||||||
|
return Buffer.from(JSON.stringify(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize a state data to commercial paper
|
||||||
|
* @param {Buffer} data to form back into the object
|
||||||
|
*/
|
||||||
|
static deserialize(data) {
|
||||||
|
return State.deserializeClass(data, CommercialPaper);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory method to create a commercial paper object
|
||||||
|
*/
|
||||||
|
static createInstance(issuer, paperNumber, issueDateTime, maturityDateTime, faceValue) {
|
||||||
|
return new CommercialPaper({ issuer, paperNumber, issueDateTime, maturityDateTime, faceValue });
|
||||||
|
}
|
||||||
|
|
||||||
|
static getClass() {
|
||||||
|
return 'org.papernet.commercialpaper';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = CommercialPaper;
|
||||||
|
|
@ -0,0 +1,156 @@
|
||||||
|
/*
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Fabric smart contract classes
|
||||||
|
const { Contract, Context } = require('fabric-contract-api');
|
||||||
|
|
||||||
|
// PaperNet specifc classes
|
||||||
|
const CommercialPaper = require('./paper.js');
|
||||||
|
const PaperList = require('./paperlist.js');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A custom context provides easy access to list of all commercial papers
|
||||||
|
*/
|
||||||
|
class CommercialPaperContext extends Context {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
// All papers are held in a list of papers
|
||||||
|
this.paperList = new PaperList(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define commercial paper smart contract by extending Fabric Contract class
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class CommercialPaperContract extends Contract {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
// Unique namespace when multiple contracts per chaincode file
|
||||||
|
super('org.papernet.commercialpaper');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define a custom context for commercial paper
|
||||||
|
*/
|
||||||
|
createContext() {
|
||||||
|
return new CommercialPaperContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiate to perform any setup of the ledger that might be required.
|
||||||
|
* @param {Context} ctx the transaction context
|
||||||
|
*/
|
||||||
|
async instantiate(ctx) {
|
||||||
|
// No implementation required with this example
|
||||||
|
// It could be where data migration is performed, if necessary
|
||||||
|
console.log('Instantiate the contract');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Issue commercial paper
|
||||||
|
*
|
||||||
|
* @param {Context} ctx the transaction context
|
||||||
|
* @param {String} issuer commercial paper issuer
|
||||||
|
* @param {Integer} paperNumber paper number for this issuer
|
||||||
|
* @param {String} issueDateTime paper issue date
|
||||||
|
* @param {String} maturityDateTime paper maturity date
|
||||||
|
* @param {Integer} faceValue face value of paper
|
||||||
|
*/
|
||||||
|
async issue(ctx, issuer, paperNumber, issueDateTime, maturityDateTime, faceValue) {
|
||||||
|
|
||||||
|
// create an instance of the paper
|
||||||
|
let paper = CommercialPaper.createInstance(issuer, paperNumber, issueDateTime, maturityDateTime, faceValue);
|
||||||
|
|
||||||
|
// Smart contract, rather than paper, moves paper into ISSUED state
|
||||||
|
paper.setIssued();
|
||||||
|
|
||||||
|
// Newly issued paper is owned by the issuer
|
||||||
|
paper.setOwner(issuer);
|
||||||
|
|
||||||
|
// Add the paper to the list of all similar commercial papers in the ledger world state
|
||||||
|
await ctx.paperList.addPaper(paper);
|
||||||
|
|
||||||
|
// Must return a serialized paper to caller of smart contract
|
||||||
|
return paper.toBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Buy commercial paper
|
||||||
|
*
|
||||||
|
* @param {Context} ctx the transaction context
|
||||||
|
* @param {String} issuer commercial paper issuer
|
||||||
|
* @param {Integer} paperNumber paper number for this issuer
|
||||||
|
* @param {String} currentOwner current owner of paper
|
||||||
|
* @param {String} newOwner new owner of paper
|
||||||
|
* @param {Integer} price price paid for this paper
|
||||||
|
* @param {String} purchaseDateTime time paper was purchased (i.e. traded)
|
||||||
|
*/
|
||||||
|
async buy(ctx, issuer, paperNumber, currentOwner, newOwner, price, purchaseDateTime) {
|
||||||
|
|
||||||
|
// Retrieve the current paper using key fields provided
|
||||||
|
let paperKey = CommercialPaper.makeKey([issuer, paperNumber]);
|
||||||
|
let paper = await ctx.paperList.getPaper(paperKey);
|
||||||
|
|
||||||
|
// Validate current owner
|
||||||
|
if (paper.getOwner() !== currentOwner) {
|
||||||
|
throw new Error('Paper ' + issuer + paperNumber + ' is not owned by ' + currentOwner);
|
||||||
|
}
|
||||||
|
|
||||||
|
// First buy moves state from ISSUED to TRADING
|
||||||
|
if (paper.isIssued()) {
|
||||||
|
paper.setTrading();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check paper is not already REDEEMED
|
||||||
|
if (paper.isTrading()) {
|
||||||
|
paper.setOwner(newOwner);
|
||||||
|
} else {
|
||||||
|
throw new Error('Paper ' + issuer + paperNumber + ' is not trading. Current state = ' +paper.getCurrentState());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the paper
|
||||||
|
await ctx.paperList.updatePaper(paper);
|
||||||
|
return paper.toBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redeem commercial paper
|
||||||
|
*
|
||||||
|
* @param {Context} ctx the transaction context
|
||||||
|
* @param {String} issuer commercial paper issuer
|
||||||
|
* @param {Integer} paperNumber paper number for this issuer
|
||||||
|
* @param {String} redeemingOwner redeeming owner of paper
|
||||||
|
* @param {String} redeemDateTime time paper was redeemed
|
||||||
|
*/
|
||||||
|
async redeem(ctx, issuer, paperNumber, redeemingOwner, redeemDateTime) {
|
||||||
|
|
||||||
|
let paperKey = CommercialPaper.makeKey([issuer, paperNumber]);
|
||||||
|
|
||||||
|
let paper = await ctx.paperList.getPaper(paperKey);
|
||||||
|
|
||||||
|
// Check paper is not REDEEMED
|
||||||
|
if (paper.isRedeemed()) {
|
||||||
|
throw new Error('Paper ' + issuer + paperNumber + ' already redeemed');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that the redeemer owns the commercial paper before redeeming it
|
||||||
|
if (paper.getOwner() === redeemingOwner) {
|
||||||
|
paper.setOwner(paper.getIssuer());
|
||||||
|
paper.setRedeemed();
|
||||||
|
} else {
|
||||||
|
throw new Error('Redeeming owner does not own paper' + issuer + paperNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
await ctx.paperList.updatePaper(paper);
|
||||||
|
return paper.toBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = CommercialPaperContract;
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Utility class for collections of ledger states -- a state list
|
||||||
|
const StateList = require('./../ledger-api/statelist.js');
|
||||||
|
|
||||||
|
const CommercialPaper = require('./paper.js');
|
||||||
|
|
||||||
|
class PaperList extends StateList {
|
||||||
|
|
||||||
|
constructor(ctx) {
|
||||||
|
super(ctx, 'org.papernet.commercialpaperlist');
|
||||||
|
this.use(CommercialPaper);
|
||||||
|
}
|
||||||
|
|
||||||
|
async addPaper(paper) {
|
||||||
|
return this.addState(paper);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPaper(paperKey) {
|
||||||
|
return this.getState(paperKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
async updatePaper(paper) {
|
||||||
|
return this.updateState(paper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = PaperList;
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
{
|
||||||
|
"name": "papernet-js",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "Papernet Contract",
|
||||||
|
"main": "index.js",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8",
|
||||||
|
"npm": ">=5"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"lint": "eslint .",
|
||||||
|
"pretest": "npm run lint",
|
||||||
|
"test": "nyc mocha test --recursive",
|
||||||
|
"start": "fabric-chaincode-node start",
|
||||||
|
"mocha": "mocha test --recursive"
|
||||||
|
},
|
||||||
|
"engineStrict": true,
|
||||||
|
"author": "hyperledger",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"fabric-contract-api": "^1.4.0-snapshot.17",
|
||||||
|
"fabric-shim": "^1.4.0-snapshot.27"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"chai": "^4.1.2",
|
||||||
|
"chai-as-promised": "^7.1.1",
|
||||||
|
"eslint": "^4.19.1",
|
||||||
|
"mocha": "^5.2.0",
|
||||||
|
"nyc": "^12.0.2",
|
||||||
|
"sinon": "^6.0.0",
|
||||||
|
"sinon-chai": "^3.2.0"
|
||||||
|
},
|
||||||
|
"nyc": {
|
||||||
|
"exclude": [
|
||||||
|
"coverage/**",
|
||||||
|
"test/**"
|
||||||
|
],
|
||||||
|
"reporter": [
|
||||||
|
"text-summary",
|
||||||
|
"html"
|
||||||
|
],
|
||||||
|
"all": true,
|
||||||
|
"check-coverage": true,
|
||||||
|
"statements": 100,
|
||||||
|
"branches": 100,
|
||||||
|
"functions": 100,
|
||||||
|
"lines": 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const Chaincode = require('../lib/chaincode');
|
||||||
|
const { Stub } = require('fabric-shim');
|
||||||
|
|
||||||
|
require('chai').should();
|
||||||
|
const sinon = require('sinon');
|
||||||
|
|
||||||
|
describe('Chaincode', () => {
|
||||||
|
|
||||||
|
describe('#Init', () => {
|
||||||
|
|
||||||
|
it('should work', async () => {
|
||||||
|
const cc = new Chaincode();
|
||||||
|
const stub = sinon.createStubInstance(Stub);
|
||||||
|
stub.getFunctionAndParameters.returns({ fcn: 'initFunc', params: [] });
|
||||||
|
const res = await cc.Init(stub);
|
||||||
|
res.status.should.equal(Stub.RESPONSE_CODE.OK);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#Invoke', async () => {
|
||||||
|
|
||||||
|
it('should work', async () => {
|
||||||
|
const cc = new Chaincode();
|
||||||
|
const stub = sinon.createStubInstance(Stub);
|
||||||
|
stub.getFunctionAndParameters.returns({ fcn: 'initFunc', params: [] });
|
||||||
|
let res = await cc.Init(stub);
|
||||||
|
res.status.should.equal(Stub.RESPONSE_CODE.OK);
|
||||||
|
stub.getFunctionAndParameters.returns({ fcn: 'invokeFunc', params: [] });
|
||||||
|
res = await cc.Invoke(stub);
|
||||||
|
res.status.should.equal(Stub.RESPONSE_CODE.OK);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,129 @@
|
||||||
|
---
|
||||||
|
#
|
||||||
|
# The network connection profile provides client applications the information about the target
|
||||||
|
# blockchain network that are necessary for the applications to interact with it. These are all
|
||||||
|
# knowledge that must be acquired from out-of-band sources. This file provides such a source.
|
||||||
|
#
|
||||||
|
name: "basic-network"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Any properties with an "x-" prefix will be treated as application-specific, exactly like how naming
|
||||||
|
# in HTTP headers or swagger properties work. The SDK will simply ignore these fields and leave
|
||||||
|
# them for the applications to process. This is a mechanism for different components of an application
|
||||||
|
# to exchange information that are not part of the standard schema described below. In particular,
|
||||||
|
# the "x-type" property with the "hlfv1" value example below is used by Hyperledger Composer to
|
||||||
|
# determine the type of Fabric networks (v0.6 vs. v1.0) it needs to work with.
|
||||||
|
#
|
||||||
|
x-type: "hlfv1"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Describe what the target network is/does.
|
||||||
|
#
|
||||||
|
description: "The basic network"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Schema version of the content. Used by the SDK to apply the corresponding parsing rules.
|
||||||
|
#
|
||||||
|
version: "1.0"
|
||||||
|
|
||||||
|
#
|
||||||
|
# [Optional]. But most apps would have this section so that channel objects can be constructed
|
||||||
|
# based on the content below. If an app is creating channels, then it likely will not need this
|
||||||
|
# section.
|
||||||
|
#
|
||||||
|
channels:
|
||||||
|
# name of the channel
|
||||||
|
mychannel:
|
||||||
|
# Required. list of orderers designated by the application to use for transactions on this
|
||||||
|
# channel. This list can be a result of access control ("org1" can only access "ordererA"), or
|
||||||
|
# operational decisions to share loads from applications among the orderers. The values must
|
||||||
|
# be "names" of orgs defined under "organizations/peers"
|
||||||
|
orderers:
|
||||||
|
- orderer.example.com
|
||||||
|
|
||||||
|
# Required. list of peers from participating orgs
|
||||||
|
peers:
|
||||||
|
peer0.org1.example.com:
|
||||||
|
# [Optional]. will this peer be sent transaction proposals for endorsement? The peer must
|
||||||
|
# have the chaincode installed. The app can also use this property to decide which peers
|
||||||
|
# to send the chaincode install request. Default: true
|
||||||
|
endorsingPeer: true
|
||||||
|
|
||||||
|
# [Optional]. will this peer be sent query proposals? The peer must have the chaincode
|
||||||
|
# installed. The app can also use this property to decide which peers to send the
|
||||||
|
# chaincode install request. Default: true
|
||||||
|
chaincodeQuery: true
|
||||||
|
|
||||||
|
# [Optional]. will this peer be sent query proposals that do not require chaincodes, like
|
||||||
|
# queryBlock(), queryTransaction(), etc. Default: true
|
||||||
|
ledgerQuery: true
|
||||||
|
|
||||||
|
# [Optional]. will this peer be the target of the SDK's listener registration? All peers can
|
||||||
|
# produce events but the app typically only needs to connect to one to listen to events.
|
||||||
|
# Default: true
|
||||||
|
eventSource: true
|
||||||
|
|
||||||
|
#
|
||||||
|
# list of participating organizations in this network
|
||||||
|
#
|
||||||
|
organizations:
|
||||||
|
Org1:
|
||||||
|
mspid: Org1MSP
|
||||||
|
|
||||||
|
peers:
|
||||||
|
- peer0.org1.example.com
|
||||||
|
|
||||||
|
# [Optional]. Certificate Authorities issue certificates for identification purposes in a Fabric based
|
||||||
|
# network. Typically certificates provisioning is done in a separate process outside of the
|
||||||
|
# runtime network. Fabric-CA is a special certificate authority that provides a REST APIs for
|
||||||
|
# dynamic certificate management (enroll, revoke, re-enroll). The following section is only for
|
||||||
|
# Fabric-CA servers.
|
||||||
|
certificateAuthorities:
|
||||||
|
- ca-org1
|
||||||
|
|
||||||
|
#
|
||||||
|
# List of orderers to send transaction and channel create/update requests to. For the time
|
||||||
|
# being only one orderer is needed. If more than one is defined, which one get used by the
|
||||||
|
# SDK is implementation specific. Consult each SDK's documentation for its handling of orderers.
|
||||||
|
#
|
||||||
|
orderers:
|
||||||
|
orderer.example.com:
|
||||||
|
url: grpc://localhost:7050
|
||||||
|
|
||||||
|
# these are standard properties defined by the gRPC library
|
||||||
|
# they will be passed in as-is to gRPC client constructor
|
||||||
|
grpcOptions:
|
||||||
|
ssl-target-name-override: orderer.example.com
|
||||||
|
|
||||||
|
#
|
||||||
|
# List of peers to send various requests to, including endorsement, query
|
||||||
|
# and event listener registration.
|
||||||
|
#
|
||||||
|
peers:
|
||||||
|
peer0.org1.example.com:
|
||||||
|
# this URL is used to send endorsement and query requests
|
||||||
|
url: grpc://localhost:7051
|
||||||
|
|
||||||
|
grpcOptions:
|
||||||
|
ssl-target-name-override: peer0.org1.example.com
|
||||||
|
request-timeout: 120001
|
||||||
|
|
||||||
|
# Fabric-CA is a special kind of Certificate Authority provided by Hyperledger Fabric which allows
|
||||||
|
# certificate management to be done via REST APIs. Application may choose to use a standard
|
||||||
|
# Certificate Authority instead of Fabric-CA, in which case this section would not be specified.
|
||||||
|
#
|
||||||
|
certificateAuthorities:
|
||||||
|
ca-org1:
|
||||||
|
url: http://localhost:7054
|
||||||
|
# the properties specified under this object are passed to the 'http' client verbatim when
|
||||||
|
# making the request to the Fabric-CA server
|
||||||
|
httpOptions:
|
||||||
|
verify: false
|
||||||
|
|
||||||
|
# Fabric-CA supports dynamic user enrollment via REST APIs. A "root" user, a.k.a registrar, is
|
||||||
|
# needed to enroll and invoke new users.
|
||||||
|
registrar:
|
||||||
|
- enrollId: admin
|
||||||
|
enrollSecret: adminpw
|
||||||
|
# [Optional] The optional name of the CA.
|
||||||
|
caName: ca-org1
|
||||||
|
|
@ -0,0 +1,225 @@
|
||||||
|
---
|
||||||
|
#
|
||||||
|
# The network connection profile provides client applications the information about the target
|
||||||
|
# blockchain network that are necessary for the applications to interact with it. These are all
|
||||||
|
# knowledge that must be acquired from out-of-band sources. This file provides such a source.
|
||||||
|
#
|
||||||
|
name: "finance-networks"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Any properties with an "x-" prefix will be treated as application-specific, exactly like how naming
|
||||||
|
# in HTTP headers or swagger properties work. The SDK will simply ignore these fields and leave
|
||||||
|
# them for the applications to process. This is a mechanism for different components of an application
|
||||||
|
# to exchange information that are not part of the standard schema described below. In particular,
|
||||||
|
# the "x-type" property with the "hlfv1" value example below is used by Hyperledger Composer to
|
||||||
|
# determine the type of Fabric networks (v0.6 vs. v1.0) it needs to work with.
|
||||||
|
#
|
||||||
|
x-type: "hlfv1"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Describe what the target network is/does.
|
||||||
|
#
|
||||||
|
description: "A gateway connection file for the PaperNet networks"
|
||||||
|
|
||||||
|
#
|
||||||
|
# Schema version of the content. Used by the SDK to apply the corresponding parsing rules.
|
||||||
|
#
|
||||||
|
version: "1.0"
|
||||||
|
|
||||||
|
#
|
||||||
|
# The client section is SDK-specific. The sample below is for the node.js SDK
|
||||||
|
#
|
||||||
|
#client:
|
||||||
|
# Which organization does this application instance belong to? The value must be the name of an org
|
||||||
|
# defined under "organizations"
|
||||||
|
#organization: Org1
|
||||||
|
|
||||||
|
# Some SDKs support pluggable KV stores, the properties under "credentialStore"
|
||||||
|
# are implementation specific
|
||||||
|
#credentialStore:
|
||||||
|
# [Optional]. Specific to FileKeyValueStore.js or similar implementations in other SDKs. Can be others
|
||||||
|
# if using an alternative impl. For instance, CouchDBKeyValueStore.js would require an object
|
||||||
|
# here for properties like url, db name, etc.
|
||||||
|
#path: "/tmp/hfc-kvs"
|
||||||
|
|
||||||
|
# [Optional]. Specific to the CryptoSuite implementation. Software-based implementations like
|
||||||
|
# CryptoSuite_ECDSA_AES.js in node SDK requires a key store. PKCS#11 based implementations does
|
||||||
|
# not.
|
||||||
|
#cryptoStore:
|
||||||
|
# Specific to the underlying KeyValueStore that backs the crypto key store.
|
||||||
|
#path: "/tmp/hfc-cvs"
|
||||||
|
|
||||||
|
# [Optional]. Specific to Composer environment
|
||||||
|
#wallet: wallet-name
|
||||||
|
|
||||||
|
#
|
||||||
|
# [Optional]. But most apps would have this section so that channel objects can be constructed
|
||||||
|
# based on the content below. If an app is creating channels, then it likely will not need this
|
||||||
|
# section.
|
||||||
|
#
|
||||||
|
channels:
|
||||||
|
# name of the channel
|
||||||
|
papernet:
|
||||||
|
# Required. list of orderers designated by the application to use for transactions on this
|
||||||
|
# channel. This list can be a result of access control ("org1" can only access "ordererA"), or
|
||||||
|
# operational decisions to share loads from applications among the orderers. The values must
|
||||||
|
# be "names" of orgs defined under "organizations/peers"
|
||||||
|
orderers:
|
||||||
|
- orderer.magnetocorp.com
|
||||||
|
|
||||||
|
# Required. list of peers from participating orgs
|
||||||
|
peers:
|
||||||
|
peer1.magnetocorp.com:
|
||||||
|
# [Optional]. will this peer be sent transaction proposals for endorsement? The peer must
|
||||||
|
# have the chaincode installed. The app can also use this property to decide which peers
|
||||||
|
# to send the chaincode install request. Default: true
|
||||||
|
endorsingPeer: true
|
||||||
|
|
||||||
|
# [Optional]. will this peer be sent query proposals? The peer must have the chaincode
|
||||||
|
# installed. The app can also use this property to decide which peers to send the
|
||||||
|
# chaincode install request. Default: true
|
||||||
|
chaincodeQuery: true
|
||||||
|
|
||||||
|
# [Optional]. will this peer be sent query proposals that do not require chaincodes, like
|
||||||
|
# queryBlock(), queryTransaction(), etc. Default: true
|
||||||
|
ledgerQuery: true
|
||||||
|
|
||||||
|
# [Optional]. will this peer be the target of the SDK's listener registration? All peers can
|
||||||
|
# produce events but the app typically only needs to connect to one to listen to events.
|
||||||
|
# Default: true
|
||||||
|
eventSource: true
|
||||||
|
|
||||||
|
peer2.digibank.com:
|
||||||
|
endorsingPeer: true
|
||||||
|
chaincodeQuery: false
|
||||||
|
ledgerQuery: true
|
||||||
|
eventSource: true
|
||||||
|
|
||||||
|
# [Optional]. what chaincodes are expected to exist on this channel? The application can use
|
||||||
|
# this information to validate that the target peers are in the expected state by comparing
|
||||||
|
# this list with the query results of getInstalledChaincodes() and getInstantiatedChaincodes()
|
||||||
|
chaincodes:
|
||||||
|
# the format follows the "cannonical name" of chaincodes by fabric code
|
||||||
|
- example02:v1
|
||||||
|
- marbles:1.0
|
||||||
|
|
||||||
|
#
|
||||||
|
# list of participating organizations in this network
|
||||||
|
#
|
||||||
|
organizations:
|
||||||
|
Org1:
|
||||||
|
mspid: magnetocorpMSP
|
||||||
|
|
||||||
|
peers:
|
||||||
|
- peer1.magnetocorp.com
|
||||||
|
|
||||||
|
# [Optional]. Certificate Authorities issue certificates for identification purposes in a Fabric based
|
||||||
|
# network. Typically certificates provisioning is done in a separate process outside of the
|
||||||
|
# runtime network. Fabric-CA is a special certificate authority that provides a REST APIs for
|
||||||
|
# dynamic certificate management (enroll, revoke, re-enroll). The following section is only for
|
||||||
|
# Fabric-CA servers.
|
||||||
|
certificateAuthorities:
|
||||||
|
- ca-magnetocorp
|
||||||
|
|
||||||
|
# [Optional]. If the application is going to make requests that are reserved to organization
|
||||||
|
# administrators, including creating/updating channels, installing/instantiating chaincodes, it
|
||||||
|
# must have access to the admin identity represented by the private key and signing certificate.
|
||||||
|
# Both properties can be the PEM string or local path to the PEM file. Note that this is mainly for
|
||||||
|
# convenience in development mode, production systems should not expose sensitive information
|
||||||
|
# this way. The SDK should allow applications to set the org admin identity via APIs, and only use
|
||||||
|
# this route as an alternative when it exists.
|
||||||
|
adminPrivateKey:
|
||||||
|
path: commercial-paper/organization/magnetocorp/users/Admin@magnetocorp/keystore/9022d671ceedbb24af3ea69b5a8136cc64203df6b9920e26f48123fcfcb1d2e9_sk
|
||||||
|
signedCert:
|
||||||
|
path: comercial-paper/organization/magnetocorp/users/Admin@magnetocorp.com/signcerts/Admin@magnetocorp.com-cert.pem
|
||||||
|
|
||||||
|
# the profile will contain public information about organizations other than the one it belongs to.
|
||||||
|
# These are necessary information to make transaction lifecycles work, including MSP IDs and
|
||||||
|
# peers with a public URL to send transaction proposals. The file will not contain private
|
||||||
|
# information reserved for members of the organization, such as admin key and certificate,
|
||||||
|
# fabric-ca registrar enroll ID and secret, etc.
|
||||||
|
Org2:
|
||||||
|
mspid: digibankMSP
|
||||||
|
peers:
|
||||||
|
- peer1.digibank.com
|
||||||
|
certificateAuthorities:
|
||||||
|
- ca-digibank
|
||||||
|
adminPrivateKey:
|
||||||
|
path: commercial-paper/organization/digibank/users/Admin@digibank.com/keystore/5a983ddcbefe52a7f9b8ee5b85a590c3e3a43c4ccd70c7795bec504e7f74848d_sk
|
||||||
|
signedCert:
|
||||||
|
path: commercial-paper/organization/digibank/users/Admin@digibank.com/signcerts/Admin@digibank.com-cert.pem
|
||||||
|
|
||||||
|
#
|
||||||
|
# List of orderers to send transaction and channel create/update requests to. For the time
|
||||||
|
# being only one orderer is needed. If more than one is defined, which one get used by the
|
||||||
|
# SDK is implementation specific. Consult each SDK's documentation for its handling of orderers.
|
||||||
|
#
|
||||||
|
orderers:
|
||||||
|
orderer.magnetocorp.com:
|
||||||
|
url: grpcs://localhost:7050
|
||||||
|
|
||||||
|
# these are standard properties defined by the gRPC library
|
||||||
|
# they will be passed in as-is to gRPC client constructor
|
||||||
|
grpcOptions:
|
||||||
|
ssl-target-name-override: orderer.example.com
|
||||||
|
|
||||||
|
tlsCACerts:
|
||||||
|
path: comercial-paper/organization/magnetocorp/orderer/orderer.magnetocorp.com/tlscacerts/example.com-cert.pem
|
||||||
|
|
||||||
|
#
|
||||||
|
# List of peers to send various requests to, including endorsement, query
|
||||||
|
# and event listener registration.
|
||||||
|
#
|
||||||
|
peers:
|
||||||
|
peer1.magnetocorp.com:
|
||||||
|
# this URL is used to send endorsement and query requests
|
||||||
|
url: grpcs://localhost:7051
|
||||||
|
|
||||||
|
grpcOptions:
|
||||||
|
ssl-target-name-override: peer1.magnetocorp.com
|
||||||
|
request-timeout: 120
|
||||||
|
|
||||||
|
tlsCACerts:
|
||||||
|
path: certificates/magnetocorp/magnetocorp.com-cert.pem
|
||||||
|
|
||||||
|
peer1.digibank.com:
|
||||||
|
url: grpcs://localhost:8051
|
||||||
|
grpcOptions:
|
||||||
|
ssl-target-name-override: peer1.digibank.com
|
||||||
|
tlsCACerts:
|
||||||
|
path: certificates/digibank/digibank.com-cert.pem
|
||||||
|
|
||||||
|
#
|
||||||
|
# Fabric-CA is a special kind of Certificate Authority provided by Hyperledger Fabric which allows
|
||||||
|
# certificate management to be done via REST APIs. Application may choose to use a standard
|
||||||
|
# Certificate Authority instead of Fabric-CA, in which case this section would not be specified.
|
||||||
|
#
|
||||||
|
certificateAuthorities:
|
||||||
|
ca-org1:
|
||||||
|
url: https://localhost:7054
|
||||||
|
# the properties specified under this object are passed to the 'http' client verbatim when
|
||||||
|
# making the request to the Fabric-CA server
|
||||||
|
httpOptions:
|
||||||
|
verify: false
|
||||||
|
tlsCACerts:
|
||||||
|
path: commercial-paper/organization/magnetocorp/ca/magnetocorp.com-cert.pem
|
||||||
|
|
||||||
|
# Fabric-CA supports dynamic user enrollment via REST APIs. A "root" user, a.k.a registrar, is
|
||||||
|
# needed to enroll and invoke new users.
|
||||||
|
registrar:
|
||||||
|
- enrollId: admin
|
||||||
|
enrollSecret: adminpw
|
||||||
|
# [Optional] The optional name of the CA.
|
||||||
|
caName: ca-magnetocorp
|
||||||
|
|
||||||
|
ca-org2:
|
||||||
|
url: https://localhost:8054
|
||||||
|
httpOptions:
|
||||||
|
verify: false
|
||||||
|
tlsCACerts:
|
||||||
|
path: commercial-paper/organization/digibank/ca/digibank.com-cert.pem
|
||||||
|
registrar:
|
||||||
|
- enrollId: admin
|
||||||
|
enrollSecret: adminpw
|
||||||
|
# [Optional] The optional name of the CA.
|
||||||
|
caName: ca-digibank
|
||||||
Loading…
Reference in a new issue