mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-17 15:35:09 +00:00
FAB-11867 Develop Apps:Sample pt 2 -- application
Change-Id: I0897682a00be1f6ebaf8eee090872c5057dc3fba Signed-off-by: Anthony O'Dowd <a_o-dowd@uk.ibm.com>
This commit is contained in:
parent
e7a1b76edb
commit
40897861b0
5 changed files with 644 additions and 0 deletions
76
commercial-paper/application/application.js
Normal file
76
commercial-paper/application/application.js
Normal file
|
|
@ -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
commercial-paper/application/gateway/connectionProfile.yaml
Normal file
225
commercial-paper/application/gateway/connectionProfile.yaml
Normal file
|
|
@ -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
|
||||
88
commercial-paper/contract/lib/ledgerutils.js
Normal file
88
commercial-paper/contract/lib/ledgerutils.js
Normal file
|
|
@ -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
|
||||
};
|
||||
117
commercial-paper/contract/lib/paper.js
Normal file
117
commercial-paper/contract/lib/paper.js
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// Enumerate commercial paper state values
|
||||
const cpState = {
|
||||
ISSUED: 1,
|
||||
TRADING: 2,
|
||||
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
|
||||
* Class will be used by application and smart contract to define a paper
|
||||
*/
|
||||
class CommercialPaper extends State {
|
||||
|
||||
constructor(issuer, paperNumber, issueDateTime, maturityDateTime, faceValue) {
|
||||
super(`org.papernet.commercialpaper`, [issuer, paperNumber]);
|
||||
|
||||
this.issuer = issuer;
|
||||
this.paperNumber = paperNumber;
|
||||
this.owner = issuer;
|
||||
this.issueDateTime = issueDateTime;
|
||||
this.maturityDateTime = maturityDateTime;
|
||||
this.faceValue = faceValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize/deserialize commercial paper
|
||||
**/
|
||||
|
||||
serialize() {
|
||||
return Buffer.from(JSON.stringify(this));
|
||||
}
|
||||
|
||||
static deserialize(data) {
|
||||
return Object.create(new CommercialPaper, JSON.parse(data));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
CommercialPaper,
|
||||
};
|
||||
138
commercial-paper/contract/lib/papercontract.js
Normal file
138
commercial-paper/contract/lib/papercontract.js
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
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');
|
||||
|
||||
// Utility classes
|
||||
const { StateList } = require('./ledgerutils.js');
|
||||
|
||||
/**
|
||||
* Define custom context for commercial paper by extending Fabric Context class
|
||||
*/
|
||||
class CommericalPaperContext extends Context {
|
||||
|
||||
constructor() {
|
||||
// All papers held ins a list of Fabric states
|
||||
this.cpList = new StateList(this, 'org.papernet.commercialpaperlist');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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');
|
||||
}
|
||||
|
||||
// This method is called when a smart contract is instantiated
|
||||
// Often used to set up the ledger main transactions are called
|
||||
instantiate() {
|
||||
|
||||
}
|
||||
|
||||
// A custom context provides easy access to the list of commercial papers
|
||||
createContext() {
|
||||
return new CommericalPaperContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
// Smart contract, rather than paper, moves paper into ISSUED state
|
||||
cp.setIssued();
|
||||
|
||||
// Add the paper to the list of all similar commercial papers in the ledger world state
|
||||
await ctx.cpList.addState(cp);
|
||||
|
||||
return cp.serialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.makeKey([issuer, paperNumber]);
|
||||
|
||||
let cp = await ctx.cpList.getState(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 not already REDEEMED
|
||||
if (cp.IsTrading()) {
|
||||
cp.setOwner(newOwner);
|
||||
} else {
|
||||
throw new Error('Paper ' + issuer + paperNumber + ' is not trading. Current state = ' + cp.getCurrentState());
|
||||
}
|
||||
|
||||
await ctx.cpList.updateState(cp);
|
||||
return cp.deserialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.makeKey([issuer, paperNumber]);
|
||||
|
||||
let cp = await ctx.cpList.getState(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.updateState(cp);
|
||||
return cp.serialize();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = CommericalPaperContract;
|
||||
Loading…
Reference in a new issue