Skip to content

Commit

Permalink
[FAB-3568] NodeSDK - Remove nonce requirement
Browse files Browse the repository at this point in the history
Autogenerate the generate the nonce and the txId at the same time
and return both in a "TransactionID" object. Users will be able to
have a transaction value to be used for tracking and SDK will be
able to provide the correct 'nonce' for the transaction id.
Remove all user API referrences to the 'nonce' and update all
test cases required.

Change-Id: I6429733c1ee7ecae642f6cd3b0518b3cbddd1691
Signed-off-by: Bret Harrison <[email protected]>
  • Loading branch information
harrisob committed May 19, 2017
1 parent 49ed64e commit c3b5cb9
Show file tree
Hide file tree
Showing 23 changed files with 463 additions and 535 deletions.
166 changes: 69 additions & 97 deletions fabric-client/lib/Chain.js

Large diffs are not rendered by default.

92 changes: 44 additions & 48 deletions fabric-client/lib/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ var ChannelConfig = require('./ChannelConfig.js');
var Packager = require('./Packager.js');
var Peer = require('./Peer.js');
var Orderer = require('./Orderer.js');
var TransactionID = require('./TransactionID.js');
var MSP = require('./msp/msp.js');

var logger = sdkUtils.getLogger('Client.js');
Expand Down Expand Up @@ -228,7 +229,7 @@ var Client = class {
}

/**
* Build a new MSP with the definition.
* Create a new MSP with the definition.
* @parm {Object} which has the following the following fields:
* <br>`id`: {string} value for the identifier of this instance
* <br>`rootCerts`: array of {@link Identity} representing trust anchors for validating
Expand All @@ -252,6 +253,23 @@ var Client = class {
return msp;
}

/**
* Create a new transaction id object with an unique transaction id
* based on included nonce and the user context.
* Requires this client instance to have an assigned user context.
* @returns {@link TransactionID} An object that contains a transaction id
* based on the user context and also contains
* the generated nonce value.
*/
newTransactionID() {
if (typeof this._userContext === 'undefined' || this._userContext === null) {
throw new Error('This client instance must be assigned an user context');
}
let trans_id = new TransactionID(this._userContext);

return trans_id;
}

/*
* For test only
*
Expand Down Expand Up @@ -455,9 +473,7 @@ var Client = class {
if(!request.txId && !have_envelope) {
errorMsg = 'Missing txId request parameter';
}
if(!request.nonce && !have_envelope) {
errorMsg = 'Missing nonce request parameter';
}

// verify that we have an orderer configured
if(!request.orderer) {
errorMsg = 'Missing orderer request parameter';
Expand Down Expand Up @@ -498,10 +514,10 @@ var Client = class {
var proto_channel_header = Chain._buildChannelHeader(
_commonProto.HeaderType.CONFIG_UPDATE,
request.name,
request.txId
request.txId.getTransactionID()
);

var proto_header = Chain._buildHeader(userContext.getIdentity(), proto_channel_header, request.nonce);
var proto_header = Chain._buildHeader(userContext.getIdentity(), proto_channel_header, request.txId.getNonce());
var proto_payload = new _commonProto.Payload();
proto_payload.setHeader(proto_header);
proto_payload.setData(proto_config_Update_envelope.toBuffer());
Expand Down Expand Up @@ -567,14 +583,11 @@ var Client = class {
return Promise.reject( new Error('Peer is required'));
}
var self = this;
var nonce = sdkUtils.getNonce();
var userContext = this.getUserContext();
var txId = Chain.buildTransactionID(nonce, userContext);
var txId = new TransactionID(this._userContext);
var request = {
targets: [peer],
chaincodeId : 'cscc',
chaincodeId : Constants.CSCC,
txId: txId,
nonce: nonce,
fcn : 'GetChannels',
args: []
};
Expand Down Expand Up @@ -627,14 +640,11 @@ var Client = class {
return Promise.reject( new Error('Peer is required'));
}
var self = this;
var nonce = sdkUtils.getNonce();
var userContext = self.getUserContext();
var tx_id = Chain.buildTransactionID(nonce, userContext);
var txId = new TransactionID(this._userContext);
var request = {
targets: [peer],
chaincodeId : Constants.LSCC,
txId: tx_id,
nonce: nonce,
txId: txId,
fcn : 'getinstalledchaincodes',
args: []
};
Expand Down Expand Up @@ -678,21 +688,19 @@ var Client = class {
* Sends an install proposal to one or more endorsing peers.
*
* @param {Object} request - An object containing the following fields:
* <br>`chaincodePath` : required - String of the path to location of
* the source code of the chaincode
* <br>`chaincodeId` : required - String of the name of the chaincode
* <br>`chaincodeVersion` : required - String of the version of the chaincode
* <br>`chaincodePackage` : optional - Byte array of the archive content for
* the chaincode source. The archive must have a 'src'
* folder containing subfolders corresponding to the
* 'chaincodePath' field. For instance, if the chaincodePath
* is 'mycompany/myproject', then the archive must contain a
* folder at the path 'src/mycompany/myproject', where the
* GO source code resides.
* <br>`chaincodeType` : optional - Type of chaincode ['golang', 'car', 'java']
* (default 'golang')
* <br>`txId` : required - String of the transaction id
* <br>`nonce` : required - Integer of the once time number
* <br>`chaincodePath` : required - String of the path to location of
* the source code of the chaincode
* <br>`chaincodeId` : required - String of the name of the chaincode
* <br>`chaincodeVersion` : required - String of the version of the chaincode
* <br>`chaincodePackage` : optional - Byte array of the archive content for
* the chaincode source. The archive must have a 'src'
* folder containing subfolders corresponding to the
* 'chaincodePath' field. For instance, if the chaincodePath
* is 'mycompany/myproject', then the archive must contain a
* folder at the path 'src/mycompany/myproject', where the
* GO source code resides.
* <br>`chaincodeType` : optional - Type of chaincode ['golang', 'car', 'java']
* (default 'golang')
* @returns {Promise} A Promise for a `ProposalResponse`
* @see /protos/peer/proposal_response.proto
*/
Expand All @@ -716,8 +724,7 @@ var Client = class {
errorMsg = 'Missing input request object on install chaincode request';
}


if (!errorMsg) errorMsg = Chain._checkProposalRequest(request);
if (!errorMsg) errorMsg = Chain._checkProposalRequest(request, true);
if (!errorMsg) errorMsg = Chain._checkInstallRequest(request);

if (errorMsg) {
Expand Down Expand Up @@ -764,15 +771,15 @@ var Client = class {

var header, proposal;
var userContext = self.getUserContext();
var txId = Chain.buildTransactionID(request.nonce, userContext);
var txId = new TransactionID(userContext);
var channelHeader = Chain._buildChannelHeader(
_commonProto.HeaderType.ENDORSER_TRANSACTION,
'', //install does not target a channel
txId,
txId.getTransactionID(),
null,
Constants.LSCC
);
header = Chain._buildHeader(userContext.getIdentity(), channelHeader, request.nonce);
header = Chain._buildHeader(userContext.getIdentity(), channelHeader, txId.getNonce());
proposal = Chain._buildProposal(lcccSpec, header);
let signed_proposal = Chain._signProposal(userContext.getSigningIdentity(), proposal);

Expand Down Expand Up @@ -856,7 +863,7 @@ var Client = class {
}

/**
* Sets an instance of the User class as the security context of self client instance. This user’s
* Sets an instance of the User class as the security context of this client instance. This user’s
* credentials (ECert), or special transaction certificates that are derived from the user's ECert,
* will be used to conduct transactions and queries with the blockchain network.
* Upon setting the user context, the SDK saves the object in a persistence cache if the “state store”
Expand Down Expand Up @@ -1024,17 +1031,6 @@ var Client = class {
return this._stateStore;
}

/**
* Utility method to build an unique transaction id
* based on a nonce and the user context.
* @param {int} nonce - a one time use number
* @param {User} userContext - the user context
* @returns {string} An unique string
*/
static buildTransactionID(nonce, userContext) {
return Chain.buildTransactionID(nonce, userContext);
}

/**
* Returns an authorized user loaded using the
* private key and pre-enrolled certificate from files
Expand Down
2 changes: 2 additions & 0 deletions fabric-client/lib/Constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,6 @@
'use strict';

module.exports.LSCC = 'lscc';
module.exports.QSCC = 'qscc';
module.exports.CSCC = 'cscc';
module.exports.SYSTEM_CHANNEL_NAME = 'testchainid';
72 changes: 72 additions & 0 deletions fabric-client/lib/TransactionID.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
Copyright 2017 IBM All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the 'License');
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an 'AS IS' BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

'use strict';

var sdkUtils = require('./utils.js');
var logger = sdkUtils.getLogger('TransactionID.js');
var User = require('./User.js');
var hashPrimitives = require('./hash.js');


/**
* The class representing the transaction identifier. Provides for
* automatically creating the `nonce` value when an instance of this
* object is created.
*
* @class
*/
var TransactionID = class {

/**
* Builds a new tranaction Id based on a user's certificate and an automatically
* generated nonce value.
* @param {Client} clientContext An instance of {@link Client} that provides an unique
* base for this transaction id.
*/
constructor(userContext) {
logger.debug('const - start');
if (typeof userContext === 'undefined' || userContext === null) {
throw new Error('Missing userContext parameter');
}
if(!(userContext instanceof User)) {
throw new Error('Parameter "userContext" must be an instance of the "User" class');
}
this._nonce = sdkUtils.getNonce(); //nonce is in bytes
let creator_bytes = userContext.getIdentity().serialize();//same as signatureHeader.Creator
let trans_bytes = Buffer.concat([this._nonce, creator_bytes]);
let trans_hash = hashPrimitives.sha2_256(trans_bytes);
this._transaction_id = Buffer.from(trans_hash).toString();
logger.debug('const - transaction_id %s',this._transaction_id);
}

/**
* The transaction ID
*/
getTransactionID() {
return this._transaction_id;
}

/**
* The nonce value
*/
getNonce() {
return this._nonce;
}
};

module.exports = TransactionID;

6 changes: 2 additions & 4 deletions test/integration/create-configtx-channel.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,15 +131,13 @@ test('\n\n***** Configtx Built config create flow *****\n\n', function(t) {
logger.debug('\n***\n done signing \n***\n');

// build up the create request
let nonce = utils.getNonce();
let tx_id = Client.buildTransactionID(nonce, the_user);
let tx_id = client.newTransactionID();
var request = {
config: config,
signatures : signatures,
name : channel_name,
orderer : orderer,
txId : tx_id,
nonce : nonce
txId : tx_id
};

// send to create request to orderer
Expand Down
8 changes: 3 additions & 5 deletions test/integration/e2e/create-channel.js
Original file line number Diff line number Diff line change
Expand Up @@ -333,18 +333,16 @@ test('\n\n***** SDK Built config update create flow *****\n\n', function(t) {
logger.debug('\n***\n done signing \n***\n');

// build up the create request
let nonce = utils.getNonce();
let tx_id = Client.buildTransactionID(nonce, the_user);
let tx_id = client.newTransactionID();
var request = {
config: config,
signatures : signatures,
name : channel_name,
orderer : orderer,
txId : tx_id,
nonce : nonce
txId : tx_id
};

// send to create request to orderer
// send create request to orderer
return client.createChannel(request);
})
.then((result) => {
Expand Down
Loading

0 comments on commit c3b5cb9

Please sign in to comment.