-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
FAB-11867 Develop Apps:Sample pt 2 -- application
Change-Id: I0897682a00be1f6ebaf8eee090872c5057dc3fba Signed-off-by: Anthony O'Dowd <a_o-dowd@uk.ibm.com>
- Loading branch information
Showing
5 changed files
with
644 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
/* | ||
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(); | ||
|
||
} |
225 changes: 225 additions & 0 deletions
225
commercial-paper/application/gateway/connectionProfile.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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: "global-trade-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 network to be in if you want to stay in the global trade business" | ||
|
||
# | ||
# 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.example.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-org1 | ||
|
||
# [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: test/fixtures/channel/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/keystore/9022d671ceedbb24af3ea69b5a8136cc64203df6b9920e26f48123fcfcb1d2e9_sk | ||
signedCert: | ||
path: test/fixtures/channel/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/signcerts/Admin@org1.example.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-org2 | ||
adminPrivateKey: | ||
path: test/fixtures/channel/crypto-config/peerOrganizations/org2.example.com/users/Admin@org2.example.com/keystore/5a983ddcbefe52a7f9b8ee5b85a590c3e3a43c4ccd70c7795bec504e7f74848d_sk | ||
signedCert: | ||
path: test/fixtures/channel/crypto-config/peerOrganizations/org2.example.com/users/Admin@org2.example.com/signcerts/Admin@org2.example.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.example.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: test/fixtures/channel/crypto-config/ordererOrganizations/example.com/orderers/orderer.example.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: test/fixtures/channel/crypto-config/peerOrganizations/org1.example.com/ca/org1.example.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-org1 | ||
|
||
ca-org2: | ||
url: https://localhost:8054 | ||
httpOptions: | ||
verify: false | ||
tlsCACerts: | ||
path: test/fixtures/channel/crypto-config/peerOrganizations/org2.example.com/ca/org2.example.com-cert.pem | ||
registrar: | ||
- enrollId: admin | ||
enrollSecret: adminpw | ||
# [Optional] The optional name of the CA. | ||
caName: ca-org2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
/* | ||
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} 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. | ||
* 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; | ||
} | ||
|
||
/** | ||
* 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.getKey()]); | ||
let data = Utils.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([keys]) { | ||
let key = this.ctx.stub.createCompositeKey(this.name, [keys]); | ||
let data = await this.ctx.stub.getState(key); | ||
let state = Utils.deserialize(data); | ||
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.getKey()]); | ||
let data = Utils.serialize(state); | ||
await this.ctx.stub.putState(key, data); | ||
} | ||
|
||
} | ||
|
||
module.exports = { | ||
StateList | ||
}; |
Oops, something went wrong.