diff --git a/fabric-client/lib/BlockDecoder.js b/fabric-client/lib/BlockDecoder.js index 149a5f3e7e..f96a669290 100644 --- a/fabric-client/lib/BlockDecoder.js +++ b/fabric-client/lib/BlockDecoder.js @@ -67,7 +67,7 @@ data signature -- {byte[]} payload header -- {{@link Header}} - data -- {{@link Config} | {@link Transaction}} + data -- {{@link ConfigEnvelope} | {@link Transaction}} metadata metadata -- {array} #each array item has it's own layout [0] #SIGNATURES @@ -83,15 +83,15 @@ metadata * @typedef {Object} Block * * @example - * Get the block number: + * Get the block number: * var block_num = block.header.number; * * @example - * Get the number of transactions, including the invalid transactions: + * Get the number of transactions, including the invalid transactions: * var block_num = block.data.data.legnth; * * @example - * Get the Id of the first transaction in the block: + * Get the Id of the first transaction in the block: * var tx_id = block.data.data[0].payload.header.channel_header.tx_id; */ @@ -143,9 +143,12 @@ nonce -- {byte[]} */ /** - * An object that contains channel configurations. + * A ConfigEnvelope contains the channel configurations data and is the + * main content of a configuration block. Another type of blocks are those + * that contain endorser transactions, where the main content is an array + * of {@link Transaction}. *

- * A "Config" will have the following object structure. + * A "ConfigEnvelope" will have the following object structure.
 config
 	sequence -- {int}
@@ -154,9 +157,9 @@ last_update
 	signature -- {byte[]}
 	payload
 		header -- {{@link Header}}
-		data -- {{@link ConfigUpdate}}
+		data -- {{@link ConfigUpdateEnvelope}}
 
- * @typedef {Object} Config + * @typedef {Object} ConfigEnvelope */ /** @@ -211,32 +214,28 @@ actions {array} status -- {int} message -- {string} payload -- {byte[]} - endorsements -- {array} - endorser - Mspid -- {string] - IdBytes -- {byte[]} - signature -- {byte[]} + endorsements -- {{@link Endorsement}[]} * @typedef {Object} Transaction */ /** - * An object of a protobuf message "ConfigUpdate". + * An object of a protobuf message "ConfigUpdateEnvelope". *

- * A "ConfigUpdate" will have the following object structure. + * A "ConfigUpdateEnvelope" will have the following object structure.
 config_update
 	channel_id -- {string}
-	read_set -- {{@link ChannelConfig}}
-	write_set -- {{@link ChannelConfig}}
+	read_set -- {{@link ChannelConfigGroup}}
+	write_set -- {{@link ChannelConfigGroup}}
 signatures -- {array}
 	signature_header -- {{@link SignatureHeader}}
 	signature -- {byte[]}
 
- * @typedef {Object} ConfigUpdate - * @property {ChannelConfig} config_update.read_set A set of the current version numbers of all + * @typedef {Object} ConfigUpdateEnvelope + * @property {ChannelConfigGroup} config_update.read_set A set of the current version numbers of all * configuration items being updated - * @property {ChannelConfig} config_update.write_set A set of all configuration items being updated. Must have a + * @property {ChannelConfigGroup} config_update.write_set A set of all configuration items being updated. Must have a * version number one greater than the version number of the same item * in the read_set along with the new value. */ @@ -257,7 +256,7 @@ groups Orderer version -- {int} groups - <orderer_org_name> -- {{@link OrganizationConfig}} + <orderer_org_name> -- {{@link OrganizationConfigGroup}} values ConsensusType version -- {int} @@ -301,7 +300,7 @@ groups Application version -- {int} groups - <peer_org_name> -- {{@link OrganizationConfig}} + <peer_org_name> -- {{@link OrganizationConfigGroup}} values policies Admins @@ -339,10 +338,13 @@ values value name -- {string} - * @typedef {Object} ChannelConfig - * @property {OrganizationConfig} groups.Orderer.groups.<orderer_org_name> These are the orderer organizatoin names defined on the network - * @property {OrganizationConfig} groups.Application.groups.<peer_org_name> These are the peer organization names defined on the network - * @property {ImplicitMetaPolicy} policy These policies point to other policies and specify a threshold as in "ANY", "MAJORITY" or "ALL" + * @typedef {Object} ChannelConfigGroup + * @property {OrganizationConfigGroup} groups.Orderer.groups.<orderer_org_name> These are the + * orderer organizatoin names defined on the network + * @property {OrganizationConfigGroup} groups.Application.groups.<peer_org_name> These are the + * peer organization names defined on the network + * @property {ImplicitMetaPolicy} policy These policies point to other policies and specify a + * threshold as in "ANY", "MAJORITY" or "ALL" */ /** @@ -384,7 +386,26 @@ policies mod_policy -- {string} policy -- {{@link SignaturePolicy}} - * @typedef {Object} OrganizationConfig + * @typedef {Object} OrganizationConfigGroup + */ + + + /** + * An endorsement is a signature of an endorser over a proposal response. By + * producing an endorsement message, an endorser implicitly "approves" that + * proposal response and the actions contained therein. When enough + * endorsements have been collected, a transaction can be generated out of a + * set of proposal responses + *

+ * An endorsement message has the following structure: +
+endorser
+	Mspid -- {string]
+	IdBytes -- {byte[]}
+signature -- {byte[]}
+
+ * + * @typedef {Object} Endorsement */ /** @@ -489,13 +510,27 @@ policy return block; }; + /** + * @typedef {Object} ProcessedTransaction + * @property {number} validationCode - See [this list]{@link https://github.com/hyperledger/fabric/blob/v1.0.0-beta/protos/peer/transaction.proto#L125} + * for all the defined validation codes + * @property {Object} transactionEnvelope - Encapsulates the transaction and the signature over it. + * It has the following structure: +
+signature -- {byte[]}
+payload -- {}
+	header -- {{@link Header}}
+	data -- {{@link Transaction}}
+
+ */ + /** * Constructs an object containing all decoded values from the - * protobuf encoded `ProcessedTransaction` bytes + * protobuf encoded "ProcessedTransaction" bytes * * @param {byte[]} processed_transaction_bytes - The encode bytes of a protobuf - * message ProcessedTransaction - * @returns {Object} An object of the fully decoded transaction.ProcessedTransaction + * message "ProcessedTransaction" + * @returns {ProcessedTransaction} A fully decoded ProcessedTransaction object */ static decodeTransaction(processed_transaction_bytes) { if (!(processed_transaction_bytes instanceof Buffer)) { diff --git a/fabric-client/lib/Channel.js b/fabric-client/lib/Channel.js index 4a29a49fc6..94d6a939b2 100755 --- a/fabric-client/lib/Channel.js +++ b/fabric-client/lib/Channel.js @@ -56,24 +56,37 @@ const ImplicitMetaPolicy_Rule = {0: 'ANY', 1:'ALL', 2:'MAJORITY'}; var Long = require('long'); /** - * The class representing a channel with which the client SDK interacts. - * - * The “Channel” object captures settings for a channel, which is created by - * the orderers to isolate transactions delivery to peers participating on channel. - * A channel must be initialized after it has been configured with the list of peers - * and orderers. The initialization sends a get configuration block request to the - * primary orderer to retrieve the configuration settings for this channel. + * In fabric v1.0, channels are the recommended way to isolate data and maintain privacy. + *

+ * A Channel object captures the settings needed to interact with a fabric backend in the + * context of a channel. These settings including the list of participating organizations, + * represented by instances of Membership Service Providers (MSP), the list of endorsing peers, + * and an orderer. + *

+ * A client application can use the Channel object to create new channels with the orderer, + * update an existing channel, send various channel-aware requests to the peers such as + * invoking chaincodes to process transactions or queries. + *

+ * A Channel object is also responsible for verifying endorsement signatures in transaction + * proposal responses. A channel object must be initialized after it has been configured with + * the list of peers and orderers. The initialization sends a get configuration block request + * to the primary orderer to retrieve the configuration settings for this channel. * * @class - * @tutorial app-overview */ var Channel = class { /** - * @param {string} name to identify different channel instances. The naming of channel instances - * is enforced by the ordering service and must be unique within the blockchain network - * @param {Client} clientContext An instance of {@link Client} that provides operational context - * such as submitting User etc. + * Returns a new instance of the class. This is a client-side-only call. To create a new channel + * in the fabric, call [createChannel()]{@link Client#createChannel}. + * + * @param {string} name - Name to identify the channel. This value is used as the identifier + * of the channel when making channel-aware requests with the fabric, + * such as invoking chaincodes to endorse transactions. The naming of + * channels is enforced by the ordering service and must be unique within + * the fabric backend + * @param {Client} clientContext - The client instance, which provides operational context + * such as the signing identity */ constructor(name, clientContext) { // name is required @@ -106,11 +119,19 @@ var Channel = class { } /** - * Retrieve the configuration from the primary orderer and initializes this channel - * with those values. Optionally a configuration may be passed in to initialize this channel - * without making the call to the orderer. - * @param {byte[]} config_update- Optional - A serialized form of the protobuf configuration update - * @return a Promise that will resolve when the action is complete + * Initializes the channel object with the Membership Service Providers (MSPs). The channel's + * MSPs are critical in providing applications the ability to validate certificates and verify + * signatures in messages received from the fabric backend. For instance, after calling + * [sendTransactionProposal()]{@link Channel#sendTransactionProposal}, the application can + * verify the signatures in the proposal response's endorsements to ensure they have not been + * tampered with. + *

+ * This method retrieves the configuration from the orderer if no "config" parameter is passed in. + * Optionally a configuration may be passed in to initialize this channel without making the call + * to the orderer. + * + * @param {byte[]} config - Optional. An encoded (a.k.a un-decoded) byte array of the protobuf "ConfigUpdate" + * @return {Promise} A Promise that will resolve when the action is complete */ initialize(config_update) { if(config_update) { @@ -145,8 +166,8 @@ var Channel = class { /** * Get organization identifiers from the MSP's for this channel - * @returns {string[]} array of MSP identifiers representing the channel's - * participating organizations + * @returns {string[]} Array of MSP identifiers representing the channel's + * participating organizations */ getOrganizations() { logger.debug('getOrganizationUnits - start'); @@ -166,12 +187,12 @@ var Channel = class { } /** - * Set the MSP Manager for this channel - * This utility method will not normally be use as the - * `initialize()` method will read this channel's - * current configuration and reset MSPManager with - * the MSP's found. - * @param {MSPManager} the msp manager for this channel + * Set the MSP Manager for this channel. This utility method will + * not normally be use as the [initialize()]{@link Channel#initialize} + * method will read this channel's current configuration and reset + * MSPManager with the MSP's found in the channel configuration. + * + * @param {MSPManager} msp_manager - The msp manager for this channel */ setMSPManager(msp_manager) { this._msp_manager = msp_manager; @@ -186,10 +207,14 @@ var Channel = class { } /** - * Add peer endpoint to channel. - * @param {Peer} peer An instance of the Peer class that has been initialized with URL, - * TLS certificate, and enrollment certificate. - * @throws {Error} if the peer with that url already exists. + * Add the peer object to the channel object. A channel object can be optionally + * configured with a list of peer objects, which will be used when calling certain + * methods such as [sendInstantiateProposal()]{@link Channel#sendInstantiateProposal}, + * [sendUpgradeProposal()]{@link Channel#sendUpgradeProposal}, + * [sendTransactionProposal]{@link Channel#sendTransactionProposal}. + * + * @param {Peer} peer - An instance of the Peer class that has been initialized with URL + * and other gRPC options such as TLS credentials and request timeout. */ addPeer(peer) { var url = peer.getUrl(); @@ -206,8 +231,11 @@ var Channel = class { } /** - * Remove peer endpoint from channel. - * @param {Peer} peer An instance of the Peer class. + * Remove the first peer object in the channel object's list of peers + * whose endpoint url property matches the url of the peer that is + * passed in. + * + * @param {Peer} peer - An instance of the Peer class. */ removePeer(peer) { var url = peer.getUrl(); @@ -221,7 +249,7 @@ var Channel = class { } /** - * Get peers of a channel from local information. + * Returns the list of peers of this channel object. * @returns {Peer[]} The peer list on the channel. */ getPeers() { @@ -230,13 +258,12 @@ var Channel = class { } /** - * Add orderer endpoint to a channel object, this is a local-only operation. - * A channel instance may choose to use a single orderer node, which will broadcast - * requests to the rest of the orderer network. Or if the application does not trust - * the orderer nodes, it can choose to use more than one by adding them to the channel instance. - * All APIs concerning the orderer will broadcast to all orderers simultaneously. - * @param {Orderer} orderer An instance of the Orderer class. - * @throws {Error} if the orderer with that url already exists. + * Add the orderer object to the channel object, this is a client-side-only operation. + * An application may add more than one orderer object to the channel object, however + * the SDK only uses the first one in the list to send broadcast messages to the + * orderer backend. + * + * @param {Orderer} orderer - An instance of the Orderer class. */ addOrderer(orderer) { var url = orderer.getUrl(); @@ -253,8 +280,11 @@ var Channel = class { } /** - * Remove orderer endpoint from a channel object, this is a local-only operation. - * @param {Orderer} orderer An instance of the Orderer class. + * Remove the first orderer object in the channel object's list of orderers + * whose endpoint url property matches the url of the orderer that is + * passed in. + * + * @param {Orderer} orderer - An instance of the Orderer class. */ removeOrderer(orderer) { var url = orderer.getUrl(); @@ -268,20 +298,27 @@ var Channel = class { } /** - * Get orderers of a channel. + * Returns the orderers of this channel object. + * @returns {Orderer[]} The list of orderers in the channel object */ getOrderers() { return this._orderers; } /** - * Will get the genesis block from the defined orderer that may be - * used in a join request - * @param {Object} request - An object containing the following fields: - *
`txId` : required - {@link TransactionID} object with the transaction id and nonce + * @typedef {Object} OrdererRequest + * @property {TransactionId} txId + */ + + /** + * A channel's first block is called the "genesis block". This block captures the + * initial channel configuration. For a peer node to join the channel, it must be + * provided the genesis block. The method [joinChannel()]{@link Channel#joinChannel} + * calls this method under the covers to automate the acquisition of the genesis block + * and sending it to the target peer to join. * - * @returns {Promise} A Promise for a protobuf `Block` - * @see /protos/peer/proposal_response.proto + * @param {OrdererRequest} request - A transaction ID object + * @returns {Promise} A Promise for an encoded protobuf "Block" */ getGenesisBlock(request) { logger.debug('getGenesisBlock - start'); @@ -354,17 +391,49 @@ var Channel = class { } /** - * Sends a join channel proposal to one or more endorsing peers - * Will get the genesis block from the defined orderer to be used - * in the proposal. - * @param {Object} request - An object containing the following fields: - *
`targets` : required - An array of `Peer` objects that will join - * this channel - *
`block` : the genesis block of the channel - * see getGenesisBlock() method - *
`txId` : required - {@link TransactionID} object with the transaction id and nonce - * @returns {Promise} A Promise for a `ProposalResponse` - * @see /protos/peer/proposal_response.proto + * A protobuf message that gets returned by endorsing peers on proposal requests. + * The peer node runs the target chaincode, as designated by the proposal, and + * decides on whether to endorse the proposal or not, and sends back the endorsement + * result along with the [read and write sets]{@link http://hyperledger-fabric.readthedocs.io/en/latest/arch-deep-dive.html?highlight=readset#the-endorsing-peer-simulates-a-transaction-and-produces-an-endorsement-signature} + * inside the proposal response message. + * + * @typedef {Object} ProposalResponse + * @property {number} version + * @property {Timestamp} timestamp - Time the proposal was created by the submitter + * @property {Response} response + * @property {byte[]} payload - The payload of the response. It is the encoded bytes of + * the "ProposalResponsePayload" protobuf message + * @property {Endorsement} endorsement - The endorsement of the proposal, basically the + * endorser's signature over the payload + */ + + /** + * A response message indicating whether the endorsement of the proposal was successful + * + * @typedef {Object} Response + * @property {number} status - Status code. Follows [HTTP status code definitions]{@link https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} + * @property {string} message - A message associated with the response status code + * @property {byte[]} payload - A payload that can be used to include metadata with this response + */ + + /** + * @typedef {Object} JoinChannelRequest + * @property {Peer[]} targets - Required. An array of Peer objects that will + * be asked to join this channel + * @property {byte[]} block - The encoded bytes of the channel's genesis block. + * See [getGenesisBlock()]{@link Channel#getGenesisBlock} method + * @property {TransactionID} txId - Required. TransactionID object with the transaction id and nonce + */ + + /** + * For a peer node to become part of a channel, it must be sent the genesis + * block, as explained [here]{@link Channel#getGenesisBlock}. This method + * sends a join channel proposal to one or more endorsing peers. It automatically + * acquires the channel's genesis block from the channel object's orderer and + * includes the block in the proposal request. + * + * @param {JoinChannelRequest} request + * @returns {Promise} A Promise for an array of {@link ProposalResponse} from the target peers */ joinChannel(request) { logger.debug('joinChannel - start'); @@ -438,11 +507,12 @@ var Channel = class { } /** - * Queries for the current config block for this channel. - * This transaction will be made to the orderer. + * Asks the orderer for the current (latest) configuration block for this channel. + * This is similar to [getGenesisBlock()]{@link Channel#getGenesisBlock}, except + * that instead of getting block number 0 it gets the latest block that contains + * the channel configuration, and only returns the decoded {@link ConfigEnvelope}. + * * @returns {ConfigEnvelope} Object containing the configuration items. - * @see /protos/orderer/ab.proto - * @see /protos/common/configtx.proto */ getChannelConfig() { logger.debug('getChannelConfig - start for channel %s',this._name); @@ -661,12 +731,23 @@ var Channel = class { return config_items; } + /** + * @typedef {Object} BlockchainInfo + * @property {number} height - How many blocks exist on the channel's ledger + * @property {byte[]} currentBlockHash - A block hash is calculated by hashing over the concatenated + * ASN.1 encoded bytes of: the block number, previous block hash, + * and current block data hash. It's the chain of the block + * hashs that guarantees the immutability of the ledger + * @property {byte[]} previousBlockHash - The block hash of the previous block. + */ + /** * Queries for various useful information on the state of the Channel * (height, known peers). - * @param {Peer} target Optional. The peer that is the target for this query. If no target - * is passed, the query will use the first peer that was added to the channel. - * @returns {Object} With height, currently the only useful info. + * + * @param {Peer} target - Optional. The peer that is the target for this query. If no target is passed, + * the query will use the first peer that was added to the channel object. + * @returns {BlockchainInfo} With blockchain height, current block hash and previous block hash. */ queryInfo(target) { logger.debug('queryInfo - start'); @@ -733,12 +814,12 @@ var Channel = class { } /** - * Queries the ledger for Block by block hash. - * This query will be made to the primary peer. - * @param {byte[]} block hash of the Block. - * @param {Peer} target Optional. The peer that is the target for this query. If no target - * is passed, the query will use the first peer that was added to the channel. - * @returns {Object} Object containing the block. + * Queries the ledger on the target peer for a Block by block hash. + * + * @param {byte[]} block hash of the Block in question. + * @param {Peer} target - Optional. The peer to send the query to. If no target is passed, + * the query is sent to the first peer that was added to the channel object. + * @returns {Block} The block matching the hash, fully decoded into an object. */ queryBlockByHash(blockHash, target) { logger.debug('queryBlockByHash - start'); @@ -795,12 +876,12 @@ var Channel = class { } /** - * Queries the ledger for Block by block number. - * This query will be made to the primary peer. - * @param {number} blockNumber The number which is the ID of the Block. - * @param {Peer} target Optional. The peer that is the target for this query. If no target - * is passed, the query will use the first peer that was added to the channel. - * @returns {Object} Object containing the block. + * Queries the ledger on the target peer for Block by block number. + * + * @param {number} blockNumber - The number of the Block in question. + * @param {Peer} target - Optional. The peer to send this query to. If no target is passed, + * the query is sent to the first peer that was added to the channel object. + * @returns {Block} The block at the blockNumber slot in the ledger, fully decoded into an object. */ queryBlock(blockNumber, target) { logger.debug('queryBlock - start blockNumber %s',blockNumber); @@ -859,12 +940,12 @@ var Channel = class { } /** - * Queries the ledger for Transaction by number. - * This query will be made to the primary peer. - * @param tx_id The id of the transaction - * @param {Peer} target Optional. The peer that is the target for this query. If no target - * is passed, the query will use the first peer that was added to the channel. - * @returns {Object} Transaction information containing the transaction. + * Queries the ledger on the target peer for Transaction by id. + * + * @param {string} tx_id - The id of the transaction + * @param {Peer} target - Optional. The peer to send this query to. If no target is passed, + * the query is sent to the first peer that was added to the channel object. + * @returns {ProcessedTransaction} The fully decoded ProcessedTransaction object. */ queryTransaction(tx_id, target) { logger.debug('queryTransaction - start transactionID %s',tx_id); @@ -922,10 +1003,11 @@ var Channel = class { } /** - * Queries the instantiated chaincodes on this channel. - * @param {Peer} target Optional. The peer that is the target for this query. If no target - * is passed, the query will use the first peer that was added to the channel. - * @returns {Object} ChaincodeQueryResponse proto + * Queries the ledger on the target peer for instantiated chaincodes on this channel. + * + * @param {Peer} target - Optional. The peer to send this query to. If no target is passed, + * the query is sent to the first peer that was added to the channel object. + * @returns {ChaincodeQueryResponse} A fully decoded ChaincodeQueryResponse object */ queryInstantiatedChaincodes(target) { logger.debug('queryInstantiatedChaincodes - start'); @@ -981,31 +1063,25 @@ var Channel = class { } /** - * Sends an instantiate proposal to one or more endorsing peers. - * - * @param {Object} request - An object containing the following fields: - *
`targets` : Optional : An array of endorsing {@link Peer} objects as the - * targets of the request. The list of endorsing peers will be used if - * this parameter is omitted. - *
`chaincodeType` : optional -- Type of chaincode ['golang', 'car', 'java'] - * (default 'golang') - *
`chaincodePath` : required - String of the path to location of - * the source code of the chaincode - *
`chaincodeId` : required - String of the name of the chaincode - *
`chaincodeVersion` : required - String of the version of the chaincode - *
`txId` : required - {@link TransactionID} object with the transaction id and nonce - *
`transientMap` : optional - map that can be used by - * the chaincode but not saved in the ledger, such as cryptographic information - * for encryption - *
`fcn` : optional - String of the function to be called on - * the chaincode once instantiated (default 'init') - *
`args` : optional - String Array arguments specific to - * the chaincode being instantiated - *
`endorsement-policy` : optional - {@link EndorsementPolicy} object for this - * chaincode. If not specified, a default policy of "a signature by any member - * from any of the organizations corresponding to the array of member service - * providers" is used - * @example "Signed by any member from one of the organizations" + * @typedef {Object} ChaincodeInstantiateUpgradeRequest + * @property {Peer[]} targets - Optional. An array of endorsing {@link Peer} objects as the targets of the + * request. The list of endorsing peers in the channel object will be used + * if this parameter is omitted. + * @property {string} chaincodeType - Optional. Type of chaincode. One of 'golang', 'car' or 'java'. + * Default is 'golang'. Note that 'java' is not supported as of v1.0. + * @property {string} chaincodeId - Required. The name of the chaincode + * @property {string} chaincodeVersion - Required. Version string of the chaincode, such as 'v1' + * @property {TransactionID} txId - Required. Object with the transaction id and nonce + * @property {map} transientMap - Optional. map that can be used by the chaincode during + * intialization, but not saved in the ledger. Data such as cryptographic information + * for encryption can be passed to the chaincode using this technique + * @property {string} fcn - Optional. The function name to be returned when calling stub.GetFunctionAndParameters() + * in the target chaincode. Default is 'init' + * @property {string[]} args - Optional. Array of string arguments to pass to the function identified by the fcn value + * @property {Object} endorsement-policy - Optional. EndorsementPolicy object for this chaincode (see examples below). If not specified, + * a default policy of "a signature by any member from any of the organizations + * corresponding to the array of member service providers" is used + * @example Endorsement policy: "Signed by any member from one of the organizations" * { * identities: [ * { role: { name: "member", mspId: "org1" }}, @@ -1015,7 +1091,7 @@ var Channel = class { * "1-of": [{ "signed-by": 0 }, { "signed-by": 1 }] * } * } - * @example "Signed by admin of the ordererOrg and any member from one of the peer organizations" + * @example Endorsement policy: "Signed by admin of the ordererOrg and any member from one of the peer organizations" * { * identities: [ * { role: { name: "member", mspId: "peerOrg1" }}, @@ -1029,35 +1105,40 @@ var Channel = class { * ] * } * } - * @returns {Promise} A Promise for a `ProposalResponse` - * @see /protos/peer/proposal_response.proto + */ + + /** + * Sends a chaincode instantiate proposal to one or more endorsing peers. + * + * A chaincode must be instantiated on a channel-by-channel basis before it can + * be used. The chaincode must first be installed on the endorsing peers where + * this chaincode is expected to run, by calling [client.installChaincode()]{@link Client#installChaincode}. + *

+ * Instantiating a chaincode is a full transaction operation, meaning it must be + * first endorsed as a proposal, then the endorsements are sent to the orderer + * to be processed for ordering and validation. When the transaction finally gets + * committed to the channel's ledger on the peers, the chaincode is then considered + * activated and the peers are ready to take requests to process transactions. + * + * @param {ChaincodeInstantiateUpgradeRequest} request + * @returns {Promise} A Promise for the {@link ProposalResponseObject} */ sendInstantiateProposal(request) { return this._sendChaincodeProposal(request, 'deploy'); } /** - * Sends an upgrade proposal to one or more endorsing peers. + * Sends a chaincode upgrade proposal to one or more endorsing peers. + * + * Upgrading a chaincode involves steps similar to instantiating a chaincode. + * The new chaincode must first be installed on the endorsing peers where + * this chaincode is expected to run. + *

+ * Similar to instantiating a chaincode, upgrading chaincodes is also a full transaction + * operation. * - * @param {Object} request - An object containing the following fields: - *
`targets` : An array of endorsing {@link Peer} objects as the - * targets of the request - *
`chaincodeType` : optional -- Type of chaincode ['golang', 'car', 'java'] - * (default 'golang') - *
`chaincodePath` : required - String of the path to location of - * the source code of the chaincode - *
`chaincodeId` : required - String of the name of the chaincode - *
`chaincodeVersion` : required - String of the version of the chaincode - *
`txId` : required - {@link TransactionID} object with the transaction id and nonce - *
`transientMap` : optional - map that can be used by - * the chaincode but not saved in the ledger, such as cryptographic information - * for encryption - *
`fcn` : optional - String of the function to be called on - * the chaincode once instantiated (default 'init') - *
`args` : optional - String Array arguments specific to - * the chaincode being instantiated - * @returns {Promise} A Promise for a `ProposalResponse` - * @see /protos/peer/proposal_response.proto + * @param {ChaincodeInstantiateUpgradeRequest} request + * @returns {Promise} A Promise for the {@link ProposalResponseObject} */ sendUpgradeProposal(request) { return this._sendChaincodeProposal(request, 'upgrade'); @@ -1156,19 +1237,31 @@ var Channel = class { ); } + /** + * @typedef {Object} ChaincodeInvokeRequest + * @property {Peer[]} targets - Optional. The peers that will receive this request, + * when not provided the list of peers added to this channel object will be used. + * @property {string} chaincodeId - Required. The id of the chaincode to process the transaction proposal + * @property {TransactionID} txId - Required. TransactionID object with the transaction id and nonce + * @property {map} transientMap - Optional. map that can be used by the chaincode but not + * saved in the ledger, such as cryptographic information for encryption + * @property {string} fcn - Optional. The function name to be returned when calling stub.GetFunctionAndParameters() + * in the target chaincode. Default is 'invoke' + * @property {string[]} args - An array of string arguments specific to the chaincode's 'Invoke' method + */ + /** * Sends a transaction proposal to one or more endorsing peers. * - * @param {Object} request - *
`targets` : optional -- The peers that will receive this request, - * when not provided the peers assigned to this channel will be used. - *
`chaincodeId` : The id of the chaincode to perform the transaction proposal - *
`txId` : required - {@link TransactionID} object with the transaction id and nonce - *
`transientMap` : optional - map that can be used by - * the chaincode but not saved in the ledger, such as cryptographic information - * for encryption - *
`args` : an array of arguments specific to the chaincode 'invoke' - * @returns {Promise} A Promise for a `ProposalResponse` + * After a chaincode gets [installed]{@link Client#installChaincode} and + * [instantiated]{@link Channel#instantiateChaincode}, it's ready to take endorsement + * proposals and participating in transaction processing. A chaincode transaction + * starts with a proposal that gets sent to the endorsing peers, which executes + * the target chaincode and decides whether the proposal should be endorsed (if it + * executes successfully) or not (if the chaincode returns an error). + * + * @param {ChaincodeInvokeRequest} request + * @returns {Promise} A Promise for the {@link ProposalResponseObject} */ sendTransactionProposal(request) { logger.debug('sendTransactionProposal - start'); @@ -1214,7 +1307,6 @@ var Channel = class { } var args = []; - // leaving this for now... but this call is always an invoke and we are not telling caller to include 'fcn' any longer args.push(Buffer.from(request.fcn ? request.fcn : 'invoke', 'utf8')); logger.debug('sendTransactionProposal - adding function arg:%s', request.fcn ? request.fcn : 'invoke'); @@ -1267,19 +1359,37 @@ var Channel = class { } /** - * Sends the orderer an endorsed proposal. - * The caller must use the proposal response returned from the endorser along - * with the original proposal request sent to the endorser. + * @typedef {Object} TransactionRequest + * @property {array} proposalResponses - An array or a single {@link ProposalResponse} objects + * containing the response from the + * [endorsement]{@link Channel#sendTransactionProposal} call + * @Property {Object} chaincodeProposal - A Proposal object containing the original + * request for endorsement(s) + */ + + /** + * Send the proposal responses that contain the endorsements of a transaction proposal + * to the orderer for further processing. This is the 2nd phase of the transaction + * lifecycle in the fabric. The orderer will globally order the transactions in the + * context of this channel and deliver the resulting blocks to the committing peers for + * validation against the chaincode's endorsement policy. When the committering peers + * successfully validate the transactions, it will mark the transaction as valid inside + * the block. After all transactions in a block have been validated, and marked either as + * valid or invalid (with a [reason code]{@link https://github.com/hyperledger/fabric/blob/v1.0.0-beta/protos/peer/transaction.proto#L125}), + * the block will be appended (committed) to the channel's ledger on the peer. + *

+ * The caller of this method must use the proposal responses returned from the endorser along + * with the original proposal that was sent to the endorser. Both of these objects are contained + * in the {@link ProposalResponseObject} returned by calls to any of the following methods: + *
  • [installChaincode()]{@link Client#installChaincode} + *
  • [sendInstantiateProposal()]{@link Channel#sendInstantiateProposal} + *
  • [sendUpgradeProposal()]{@link Channel#sendUpgradeProposal} + *
  • [sendTransactionProposal()]{@link Channel#sendTransactionProposal} * - * @param {Array} proposalResponses - An array or single {ProposalResponse} objects containing - * the response from the endorsement - * @see /protos/peer/proposal_response.proto - * @param {Proposal} chaincodeProposal - A Proposal object containing the original - * request for endorsement(s) - * @see /protos/peer/proposal.proto - * @returns {Promise} A Promise for a `BroadcastResponse`. - * This will be an acknowledgement from the orderer of successfully submitted transaction. - * @see /protos/orderer/ab.proto + * @param {TransactionRequest} request + * @returns {Promise} A Promise for a "BroadcastResponse" message returned by the orderer that contains a + * single "status" field for a standard [HTTP response code]{@link https://github.com/hyperledger/fabric/blob/v1.0.0-beta/protos/common/common.proto#L27}. + * This will be an acknowledgement from the orderer of successfully submitted transaction. */ sendTransaction(request) { logger.debug('sendTransaction - start :: channel %s',this); @@ -1293,9 +1403,6 @@ var Channel = class { if (!request.proposal) { errorMsg = 'Missing "proposal" parameter in transaction request'; } - if (!request.header) { - errorMsg = 'Missing "header" parameter in transaction request'; - } } else { errorMsg = 'Missing input request object on the proposal request'; } @@ -1381,20 +1488,25 @@ var Channel = class { /** * Sends a proposal to one or more endorsing peers that will be handled by the chaincode. - * This request will be presented to the chaincode 'invoke' and must understand - * from the arguments that this is a query request. The chaincode must also return + * In fabric v1.0, there is no difference in how the endorsing peers process a request + * to invoke a chaincode for transaction vs. to invoke a chaincode for query. All requests + * will be presented to the target chaincode's 'Invoke' method which must be implemented to + * understand from the arguments that this is a query request. The chaincode must also return * results in the byte array format and the caller will have to be able to decode - * these results + * these results. * - * @param {Object} request A JSON object with the following - *
    targets : An array or single Endorsing {@link Peer} objects as the targets of the request - *
    chaincodeId : The id of the chaincode to perform the query - *
    `args` : an array of arguments specific to the chaincode 'innvoke' - * that represent a query invocation on that chaincode - *
    `transientMap` : optional - map that can be used by - * the chaincode but not saved in the ledger, such as cryptographic information - * for encryption - * @returns {Promise} A Promise for an array of byte array results from the chaincode on all Endorsing Peers + * @param {ChaincodeInvokeRequest} request - Query requests use the same request objects as for + * transaction invocation requests + * @returns {Promise} A Promise for an array of byte array results returned from the chaincode + * on all Endorsing Peers + * @example + * Get the list of query results returned by the chaincode + * channel.queryByChaincode(request) + * .then((response_payloads) => { + * for(let i = 0; i < response_payloads.length; i++) { + * console.log(util.format('Query result from peer [%s]: %s', i, response_payloads[i].toString('utf8'))); + * } + * }); */ queryByChaincode(request) { logger.debug('queryByChaincodel - start'); @@ -1448,21 +1560,22 @@ var Channel = class { } /** - * Utility method to verify a single proposal response. - * Requires that the initialize method of this channel has been - * executed to load this channel's MSPs. The MSPs will have the + * Utility method to verify a single proposal response. It checks the + * following aspects: + *
  • The endorser's identity belongs to a legitimate MSP of the channel + * and can be successfully deserialized + *
  • The endorsement signature can be successfully verified with the + * endorser's identity certificate + *

    + * This method requires that the initialize method of this channel object + * has been called to load this channel's MSPs. The MSPs will have the * trusted root certificates for this channel. - * The verifications performed are - * - validate that the proposal endorsement's signer is trusted - * - verify that the endorsement signature matches the signer's - * claimed identity * - * @param {ProposalResponse} The endorsement response from the peer, - * includes the endorser certificate and signature over the - * proposal, endorsement result and endorser certificate. - * @see /protos/peer/proposal_reponse.proto - * @returns {boolean} a boolean value of true when both the identity and - * the signature are valid, false otherwise. + * @param {ProposalResponse} proposal_response - The endorsement response from the peer, + * includes the endorser certificate and signature over the + * proposal + endorsement result + endorser certificate. + * @returns {boolean} A boolean value of true when both the identity and + * the signature are valid, false otherwise. */ verifyProposalResponse(proposal_response) { logger.debug('verifyProposalResponse - start'); @@ -1525,8 +1638,8 @@ var Channel = class { * the same endorsement result write sets. * This will validate that the endorsing peers all agree on the result * of the chaincode execution. + * * @param {ProposalResponse[]} The proposal responses from all endorsing peers - * @see /protos/peer/proposal_reponse.proto * @returns {boolean} True when all proposals compare equally, false otherwise. */ compareProposalResponseResults(proposal_responses) { @@ -1562,8 +1675,8 @@ var Channel = class { } /** - * return a printable representation of this object - */ + * return a printable representation of this channel object + */ toString() { let orderers = ''; for (let i = 0; i < this._orderers.length; i++) { diff --git a/fabric-client/lib/Client.js b/fabric-client/lib/Client.js index 77a083f658..7f3c8f3a30 100644 --- a/fabric-client/lib/Client.js +++ b/fabric-client/lib/Client.js @@ -637,13 +637,17 @@ var Client = class extends BaseClient { * folder 'src/mycompany.com/myproject/mypackage/mychaincode', where the * GO source code resides. * @property {string} chaincodeType - Optional. Type of chaincode. One of 'golang', 'car' or 'java'. - * Default is 'golang'. + * Default is 'golang'. Note that 'java' is not supported as of v1.0. */ /** - * @typedef {array} ProposalReponseObject + * All calls to the endorsing peers for proposal endorsement return this standard + * array of objects. + * + * @typedef {array} ProposalResponseObject * @property {array} index:0 - Array of ProposalResponse objects from the endorsing peers - * @property {Object} index:1 - The original Proposal object + * @property {Object} index:1 - The original Proposal object needed when sending the transaction + * request to the orderer */ /** diff --git a/test/unit/channel.js b/test/unit/channel.js index f014939ae7..ae406aa6db 100644 --- a/test/unit/channel.js +++ b/test/unit/channel.js @@ -965,21 +965,7 @@ test('\n\n ** Channel sendTransaction() tests **\n\n', function (t) { } }); - var p4 = _channel.sendTransaction({ - proposalResponses: 'blah', - proposal: 'blah' - }) - .then(function () { - t.fail('Should not have been able to resolve the promise because of missing parameters'); - }, function (err) { - if (err.message.indexOf('Missing "header" parameter in transaction request') >= 0) { - t.pass('Successfully caught missing header error'); - } else { - t.fail('Failed to catch the missing header error. Error: ' + err.stack ? err.stack : err); - } - }); - - Promise.all([p1, p2, p3, p4]) + Promise.all([p1, p2, p3]) .then( function (data) { t.end();