diff --git a/build/tasks/watch.js b/build/tasks/watch.js index ba58a15171..0e70d75f7d 100644 --- a/build/tasks/watch.js +++ b/build/tasks/watch.js @@ -10,7 +10,10 @@ gulp.task('watch', function () { watch([ 'hfc/lib/**/*', - 'hfc-cop/lib/**/*' + 'hfc/index.js', + 'hfc/config/**/*', + 'hfc-cop/lib/**/*', + 'hfc-cop/config/**/*' ], { ignoreInitial: false, base: './' }) .pipe(debug()) .pipe(gulp.dest('node_modules')); diff --git a/hfc-cop/config/default.json b/hfc-cop/config/default.json index f9d144448a..750c64de22 100644 --- a/hfc-cop/config/default.json +++ b/hfc-cop/config/default.json @@ -2,7 +2,7 @@ "request-timeout" : 3000, "tcert-batch-size" : 10, "crypto-asymmetric-key-algo": "ECDSA", - "crypto-hash-algo": "SHA3", - "crypto-keysize": 384, + "crypto-hash-algo": "SHA2", + "crypto-keysize": 256, "crypto-suite": "./impl/CryptoSuite_ECDSA_AES.js", } diff --git a/hfc/config/default.json b/hfc/config/default.json index 5e70335fa6..9374f76da1 100644 --- a/hfc/config/default.json +++ b/hfc/config/default.json @@ -2,8 +2,8 @@ "request-timeout" : 3000, "tcert-batch-size" : 10, "crypto-asymmetric-key-algo": "ECDSA", - "crypto-hash-algo": "SHA3", - "crypto-keysize": 384, + "crypto-hash-algo": "SHA2", + "crypto-keysize": 256, "crypto-suite": "./impl/CryptoSuite_ECDSA_AES.js", "key-value-store": "./impl/FileKeyValueStore.js", "member-service": "./impl/FabricCOPImpl.js", diff --git a/hfc/index.js b/hfc/index.js index e4cd43731a..f3de967bc0 100644 --- a/hfc/index.js +++ b/hfc/index.js @@ -198,3 +198,29 @@ module.exports.getConfigSetting = function(name, default_value) { return utils.getConfigSetting(name, default_value); }; +/** + * Builds an unique transaction ID based on the values in + * the request object. + * + * @param {Object} request - An object with the values to be used + * to build an unique transaction ID. + * @returns {String} An unique transaction ID + */ +module.exports.buildTransactionID = function(request) { + + return utils.buildTransactionID(request); +}; + +/** + * Gets a random number for a one time use + * + * @param {int} length - Optional value to control the length of the number. + * The configuration setting 'nonce-size' will be used if not + * passed in. + * @return {int} Random number of the specified length + */ +module.exports.getNonce = function(length) { + + return utils.buildTransactionID(length); +}; + diff --git a/hfc/lib/Chain.js b/hfc/lib/Chain.js index 6c48a0893e..330e8a3549 100644 --- a/hfc/lib/Chain.js +++ b/hfc/lib/Chain.js @@ -208,7 +208,9 @@ var Chain = class { member.restoreState() .then( function() { - self._members[name] = member; + if (member.isEnrolled()) { + self._members[name] = member; + } logger.debug('Requested member "%s" loaded from key value store', name); return resolve(member); } diff --git a/hfc/lib/Member.js b/hfc/lib/Member.js index a94a6d0609..65c41bffb7 100644 --- a/hfc/lib/Member.js +++ b/hfc/lib/Member.js @@ -29,7 +29,6 @@ var _ccProto = grpc.load(__dirname + '/protos/peer/chaincode.proto').protos; var _ccProposalProto = grpc.load(__dirname + '/protos/peer/chaincode_proposal.proto').protos; var _ccTransProto = grpc.load(__dirname + '/protos/peer/chaincode_transaction.proto').protos; var _transProto = grpc.load(__dirname + '/protos/peer/fabric_transaction.proto').protos; -var _headerProto = grpc.load(__dirname + '/protos/peer/fabric_transaction_header.proto').protos; var _proposalProto = grpc.load(__dirname + '/protos/peer/fabric_proposal.proto').protos; var _responseProto = grpc.load(__dirname + '/protos/peer/fabric_proposal_response.proto').protos; var _commonProto = grpc.load(__dirname + '/protos/common/common.proto').common; @@ -241,18 +240,34 @@ var Member = class { * This will be an acknowledgement from the orderer of successfully submitted transaction. * @see the ./proto/atomicbroadcast/ab.proto */ - sendTransaction(proposalResponses, chaincodeProposal) { + sendTransaction(request) { logger.debug('Member.sendTransaction - start :: chain '+this._chain); + var errorMsg = null; - // Verify that data is being passed in - if (!proposalResponses) { - logger.error('Member.sendTransaction - input proposalResponse missing'); - return Promise.reject(new Error('Missing proposalResponse object parameter')); + if (request) { + // Verify that data is being passed in + if (!request.proposalResponses) { + errorMsg = 'Missing "proposalResponse" parameter in transaction request'; + } + 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'; } - if (!chaincodeProposal) { - logger.error('Member.sendTransaction - input chaincodeProposal missing'); - return Promise.reject(new Error('Missing chaincodeProposal object parameter')); + + if(errorMsg) { + logger.error('Member.sendTransaction error '+ errorMsg); + return Promise.reject(new Error(errorMsg)); } + + let proposalResponses = request.proposalResponses; + let chaincodeProposal = request.proposal; + let header = request.header; + // verify that we have an orderer configured if(!this._chain.getOrderer()) { logger.error('Member.sendTransaction - no orderer defined'); @@ -274,36 +289,43 @@ var Member = class { endorsements.push(proposalResponse.endorsement); } -// logger.debug('Member.sendTransaction - proposalResponse %j', proposalResponse); -// logger.debug('Member.sendTransaction - chaincodePropsoal %j', chaincodeProposal); - var chaincodeEndorsedAction = new _ccTransProto.ChaincodeEndorsedAction(); chaincodeEndorsedAction.setProposalResponsePayload(proposalResponse.payload); chaincodeEndorsedAction.setEndorsements(endorsements); var chaincodeActionPayload = new _ccTransProto.ChaincodeActionPayload(); chaincodeActionPayload.setAction(chaincodeEndorsedAction); - chaincodeActionPayload.setChaincodeProposalPayload(chaincodeProposal.payload); + var chaincodeProposalPayloadNoTrans = _ccProposalProto.ChaincodeProposalPayload.decode(chaincodeProposal.payload); + chaincodeProposalPayloadNoTrans.transient = null; + var payload_hash = this._chain.cryptoPrimitives.hash(chaincodeProposalPayloadNoTrans.toBuffer()); + chaincodeActionPayload.setChaincodeProposalPayload(Buffer.from(payload_hash, 'hex')); + + //let header = Member._buildHeader(this._enrollment.certificate, request.chainId, request.chaincodeId, request.txId, request.nonce); var transactionAction = new _transProto.TransactionAction(); - transactionAction.setHeader(chaincodeProposal.header); + transactionAction.setHeader(header.getSignatureHeader().toBuffer()); transactionAction.setPayload(chaincodeActionPayload.toBuffer()); var actions = []; actions.push(transactionAction); - var transaction2 = new _transProto.Transaction2(); - transaction2.setActions(actions); + var transaction = new _transProto.Transaction(); + transaction.setActions(actions); - let header = Member._buildHeader(this._enrollment.certificate, null); var payload = new _commonProto.Payload(); payload.setHeader(header); - payload.setData(transaction2.toBuffer()); + payload.setData(transaction.toBuffer()); + + let payload_bytes = payload.toBuffer(); + // sign the proposal + let sig = this._chain.cryptoPrimitives.sign(this._enrollment.privateKey, payload_bytes); + let signature = Buffer.from(sig.toDER()); // building manually or will get protobuf errors on send var envelope = { - payload : payload.toBuffer() + signature: signature, + payload : payload_bytes }; var orderer = this._chain.getOrderer(); @@ -317,6 +339,9 @@ var Member = class { *
`targets` : required - An array or single Endorsing {@link Peer} objects as the targets of the request *
`chaincodePath` : required - String of the path to location of the source code of the chaincode *
`chaincodeId` : required - String of the name of the chaincode + *
`chainId` : required - String of the name of the chain + *
`txId` : required - String of the transaction id + *
`nonce` : required - Integer of the once time number *
`fcn` : optional - String of the function to be called on the chaincode once deployed (default 'init') *
`args` : optional - String Array arguments specific to the chaincode being deployed *
`dockerfile-contents` : optional - String defining the @@ -324,21 +349,18 @@ var Member = class { * @see /protos/peer/fabric_proposal_response.proto */ sendDeploymentProposal(request) { - // Verify that chaincodePath is being passed - if (!request.chaincodePath || request.chaincodePath === '') { - logger.error('Invalid input parameter to "sendDeploymentProposal": must have "chaincodePath"'); - return Promise.reject(new Error('Missing chaincodePath in Deployment proposal request')); - } + var errorMsg = null; - if(!request.chaincodeId) { - logger.error('Missing chaincodeId in the Deployment proposal request'); - return Promise.reject(new Error('Missing chaincodeId in the Deployment proposal request')); + // Verify that chaincodePath is being passed + if (request && (!request.chaincodePath || request.chaincodePath === '')) { + errorMsg = 'Missing chaincodePath parameter in Deployment proposal request'; + } else { + errorMsg = Member._checkProposalRequest(request); } - // verify that the caller has included a peer object - if(!request.targets) { - logger.error('Invalid input parameter to "sendDeploymentProposal": must have "targets" object'); - return Promise.reject(new Error('Missing "targets" for the endorsing peer objects in the Deployment proposal request')); + if(errorMsg) { + logger.error('Member.sendDeploymentProposal error '+ errorMsg); + return Promise.reject(new Error(errorMsg)); } // args is optional because some chaincode may not need any input parameters during initialization @@ -395,13 +417,14 @@ var Member = class { } }; - let proposal = self._buildProposal(lcccSpec, 'lccc'); - let signed_proposal = self._signProposal(proposal); + let header = Member._buildHeader(self._enrollment.certificate, request.chainId, 'lccc', request.txId, request.nonce); + let proposal = self._buildProposal(lcccSpec, header); + let signed_proposal = self._signProposal(self._enrollment, proposal); return Member._sendPeersProposal(request.targets, signed_proposal) .then( function(responses) { - resolve([responses, proposal]); + resolve([responses, proposal, header]); } ).catch( function(err) { @@ -427,27 +450,25 @@ var Member = class { * @param {Object} request *
`targets` : An array or single Endorsing {@link Peer} objects as the targets of the request *
`chaincodeId` : The id of the chaincode to perform the transaction proposal + *
`chainId` : required - String of the name of the chain + *
`txId` : required - String of the transaction id + *
`nonce` : required - Integer of the once time number *
`args` : an array of arguments specific to the chaincode 'innvoke' * @returns {Promise} A Promise for a `ProposalResponse` */ sendTransactionProposal(request) { logger.debug('Member.sendTransactionProposal - start'); - - // verify that the caller has included a peer object - if(!request.targets) { - logger.error('Missing "targets" endorser peer objects in the Transaction proposal request'); - return Promise.reject(new Error('Missing "targets" for endorser peer objects in the Transaction proposal request')); - } - - if(!request.chaincodeId) { - logger.error('Missing chaincodeId in the Transaction proposal request'); - return Promise.reject(new Error('Missing chaincodeId in the Transaction proposal request')); + var errorMsg = null; + // args is not optional because we need for transaction to execute + if (request && !request.args) { + errorMsg = 'Missing "args" in Transaction proposal request'; + } else { + errorMsg = Member._checkProposalRequest(request); } - // args is not optional because we need for transaction to execute - if (!request.args) { - logger.error('Missing arguments in Transaction proposal request'); - return Promise.reject(new Error('Missing arguments in Transaction proposal request')); + if(errorMsg) { + logger.error('Member.sendTransactionProposal error '+ errorMsg); + return Promise.reject(new Error(errorMsg)); } var args = []; @@ -470,13 +491,14 @@ var Member = class { } }; - let proposal = this._buildProposal(invokeSpec, request.chaincodeId); - let signed_proposal = this._signProposal(proposal); + let header = Member._buildHeader(this._enrollment.certificate, request.chainId, request.chaincodeId, request.txId, request.nonce); + let proposal = this._buildProposal(invokeSpec, header); + let signed_proposal = this._signProposal(this._enrollment, proposal); return Member._sendPeersProposal(request.targets, signed_proposal) .then( function(responses) { - return Promise.resolve([responses,proposal]); + return Promise.resolve([responses, proposal, header]); } ).catch( function(err) { @@ -532,26 +554,25 @@ var Member = class { /** * @private */ - static _buildHeader(creator, chaincode_id) { + static _buildHeader(creator, chain_id, chaincode_id, tx_id, nonce) { let chainHeader = new _commonProto.ChainHeader(); chainHeader.setType(_commonProto.HeaderType.ENDORSER_TRANSACTION); - chainHeader.setVersion(0); //TODO what should this be + chainHeader.setTxID(tx_id.toString()); + chainHeader.setChainID(Buffer.from(chain_id)); if(chaincode_id) { let chaincodeID = new _ccProto.ChaincodeID(); chaincodeID.setName(chaincode_id); let headerExt = new _ccProposalProto.ChaincodeHeaderExtension(); headerExt.setChaincodeID(chaincodeID); -// headerExt.setPayloadVisibility(TODO); - chainHeader.setChainID(chaincodeID.toBuffer()); chainHeader.setExtension(headerExt.toBuffer()); } let signatureHeader = new _commonProto.SignatureHeader(); signatureHeader.setCreator(Buffer.from(creator)); - signatureHeader.setNonce(crypto.randomBytes(sdkUtils.getConfigSetting('nonce-size', 24))); + signatureHeader.setNonce(nonce); let header = new _commonProto.Header(); header.setSignatureHeader(signatureHeader); @@ -564,7 +585,7 @@ var Member = class { /** * @private */ - _buildProposal(invokeSpec, chaincode_id) { + _buildProposal(invokeSpec, header) { // construct the ChaincodeInvocationSpec let cciSpec = new _ccProto.ChaincodeInvocationSpec(); cciSpec.setChaincodeSpec(invokeSpec); @@ -572,9 +593,7 @@ var Member = class { let cc_payload = new _ccProposalProto.ChaincodeProposalPayload(); cc_payload.setInput(cciSpec.toBuffer()); - //cc_payload.setTransient(n/a); // TODO application-level confidentiality related - - let header = Member._buildHeader(this._enrollment.certificate, chaincode_id); + //cc_payload.setTransient(null); // TODO application-level confidentiality related // proposal -- will switch to building the proposal once the signProposal is used let proposal = new _proposalProto.Proposal(); @@ -632,11 +651,11 @@ var Member = class { /** * @private */ - _signProposal(proposal) { + _signProposal(enrollment, proposal) { let proposal_bytes = proposal.toBuffer(); // sign the proposal - let sig = this._chain.cryptoPrimitives.sign(this._enrollment.privateKey, proposal_bytes); - let signature = new Buffer(sig.toDER()); + let sig = this._chain.cryptoPrimitives.sign(enrollment.privateKey, proposal_bytes); + let signature = Buffer.from(sig.toDER()); logger.debug('_signProposal - signature::'+JSON.stringify(signature)); @@ -704,6 +723,32 @@ var Member = class { return JSON.stringify(state); } + + /* + * @private + */ + static _checkProposalRequest(request) { + var errorMsg = null; + + if(request) { + if(!request.chaincodeId) { + errorMsg = 'Missing "chaincodeId" parameter in the proposal request'; + } else if(!request.chainId) { + errorMsg = 'Missing "chainId" parameter in the proposal request'; + } else if(!request.targets) { + errorMsg = 'Missing "targets" parameter in the proposal request'; + } else if(!request.txId) { + errorMsg = 'Missing "txId" parameter in the proposal request'; + } else if(!request.chainId) { + errorMsg = 'Missing "chainId" parameter in the proposal request'; + } else if(!request.nonce) { + errorMsg = 'Missing "nonce" parameter in the proposal request'; + } + } else { + errorMsg = 'Missing input request object on the proposal request'; + } + return errorMsg; + } }; function toKeyValueStoreName(name) { diff --git a/hfc/lib/protos/common/common.proto b/hfc/lib/protos/common/common.proto index 1d16831e19..1484a3bb62 100644 --- a/hfc/lib/protos/common/common.proto +++ b/hfc/lib/protos/common/common.proto @@ -59,6 +59,14 @@ message ChainHeader { // Identifier of the chain this message is bound for bytes chainID = 4; + // An unique identifier that is used end-to-end. + // - set by higher layers such as end user or SDK + // - passed to the endorser (which will check for uniqueness) + // - as the header is passed along unchanged, it will be + // be retrieved by the committer (uniqueness check here as well) + // - to be stored in the ledger + string txID = 5; + // The epoch in which this header was generated, where epoch is defined based on block height // Epoch in which the response has been generated. This field identifies a // logical window of time. A proposal response is accepted by a peer only if @@ -66,10 +74,10 @@ message ChainHeader { // 1. the epoch specified in the message is the current epoch // 2. this message has been only seen once during this epoch (i.e. it hasn't // been replayed) - uint64 epoch = 5; + uint64 epoch = 6; // Extension that may be attached based on the header type - bytes extension = 6; + bytes extension = 7; } message SignatureHeader { diff --git a/hfc/lib/protos/peer/api.proto b/hfc/lib/protos/peer/api.proto deleted file mode 100644 index 6fa87c3758..0000000000 --- a/hfc/lib/protos/peer/api.proto +++ /dev/null @@ -1,58 +0,0 @@ -/* -Copyright IBM Corp. 2016 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. -*/ - -syntax = "proto3"; - -package protos; - -import "fabric.proto"; -import "../google/protobuf/empty.proto"; - -option go_package = "github.com/hyperledger/fabric/protos/peer"; - -// Interface exported by the server. -service Openchain { - - // GetBlockchainInfo returns information about the blockchain ledger such as - // height, current block hash, and previous block hash. - rpc GetBlockchainInfo(google.protobuf.Empty) returns (BlockchainInfo) {} - - // GetBlockByNumber returns the data contained within a specific block in the - // blockchain. The genesis block is block zero. - rpc GetBlockByNumber(BlockNumber) returns (Block) {} - - // GetBlockCount returns the current number of blocks in the blockchain data - // structure. - rpc GetBlockCount(google.protobuf.Empty) returns (BlockCount) {} - - // GetPeers returns a list of all peer nodes currently connected to the target - // peer. - rpc GetPeers(google.protobuf.Empty) returns (PeersMessage) {} -} - -// Specifies the block number to be returned from the blockchain. -message BlockNumber { - - uint64 number = 1; - -} - -// Specifies the current number of blocks in the blockchain. -message BlockCount { - - uint64 count = 1; - -} diff --git a/hfc/lib/protos/peer/chaincode.proto b/hfc/lib/protos/peer/chaincode.proto index 18971a62dd..cc66e0cfda 100644 --- a/hfc/lib/protos/peer/chaincode.proto +++ b/hfc/lib/protos/peer/chaincode.proto @@ -106,20 +106,6 @@ message ChaincodeInvocationSpec { string idGenerationAlg = 2; } -// This structure contain transaction data that we send to the chaincode -// container shim and allow the chaincode to access through the shim interface. -// TODO: Consider remove this message and just pass the transaction object -// to the shim and/or allow the chaincode to query transactions. -message ChaincodeSecurityContext { - bytes callerCert = 1; - bytes callerSign = 2; - bytes payload = 3; - bytes binding = 4; - bytes metadata = 5; - bytes parentMetadata = 6; - google.protobuf.Timestamp txTimestamp = 7; // transaction timestamp -} - message ChaincodeMessage { enum Type { @@ -135,22 +121,17 @@ message ChaincodeMessage { PUT_STATE = 9; DEL_STATE = 10; INVOKE_CHAINCODE = 11; - INVOKE_QUERY = 12; RESPONSE = 13; - QUERY = 14; - QUERY_COMPLETED = 15; - QUERY_ERROR = 16; - RANGE_QUERY_STATE = 17; - RANGE_QUERY_STATE_NEXT = 18; - RANGE_QUERY_STATE_CLOSE = 19; - KEEPALIVE = 20; + RANGE_QUERY_STATE = 14; + RANGE_QUERY_STATE_NEXT = 15; + RANGE_QUERY_STATE_CLOSE = 16; + KEEPALIVE = 17; } Type type = 1; google.protobuf.Timestamp timestamp = 2; bytes payload = 3; string txid = 4; - ChaincodeSecurityContext securityContext = 5; //event emmited by chaincode. Used only with Init or Invoke. // This event is then stored (currently) diff --git a/hfc/lib/protos/peer/devops.proto b/hfc/lib/protos/peer/devops.proto deleted file mode 100644 index e78fd462fd..0000000000 --- a/hfc/lib/protos/peer/devops.proto +++ /dev/null @@ -1,96 +0,0 @@ -/* -Copyright IBM Corp. 2016 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. -*/ - -syntax = "proto3"; - -package protos; - -option go_package = "github.com/hyperledger/fabric/protos/peer"; - -import "chaincode.proto"; -import "fabric.proto"; - -// Interface exported by the server. -service Devops { - // Log in - passed Secret object and returns Response object, where - // msg is the security context to be used in subsequent invocations - rpc Login(Secret) returns (Response) {} - - // Build the chaincode package. - rpc Build(ChaincodeSpec) returns (ChaincodeDeploymentSpec) {} - - // Deploy the chaincode package to the chain. - rpc Deploy(ChaincodeSpec) returns (ChaincodeDeploymentSpec) {} - - // Invoke chaincode. - rpc Invoke(ChaincodeInvocationSpec) returns (Response) {} - - // Query chaincode. - rpc Query(ChaincodeInvocationSpec) returns (Response) {} - - // Retrieve a TCert. - rpc EXP_GetApplicationTCert(Secret) returns (Response) {} - - // Prepare for performing a TX, which will return a binding that can later be used to sign and then execute a transaction. - rpc EXP_PrepareForTx(Secret) returns (Response) {} - - // Prepare for performing a TX, which will return a binding that can later be used to sign and then execute a transaction. - rpc EXP_ProduceSigma(SigmaInput) returns (Response) {} - - // Execute a transaction with a specific binding - rpc EXP_ExecuteWithBinding(ExecuteWithBinding) returns (Response) {} -} - -// Secret is a temporary object to establish security with the Devops. -// A better solution using certificate will be introduced later -message Secret { - string enrollId = 1; - string enrollSecret = 2; -} - -message SigmaInput { - Secret secret = 1; - bytes appTCert = 2; - bytes data = 3; -} - -message ExecuteWithBinding { - ChaincodeInvocationSpec chaincodeInvocationSpec = 1; - bytes binding = 2; -} - -message SigmaOutput { - bytes tcert = 1; - bytes sigma = 2; - bytes asn1Encoding = 3; -} - -message BuildResult { - - enum StatusCode { - UNDEFINED = 0; - SUCCESS = 1; - FAILURE = 2; - } - - StatusCode status = 1; - string msg = 2; - ChaincodeDeploymentSpec deploymentSpec = 3; -} - -message TransactionRequest { - string transactionUuid = 1; -} diff --git a/hfc/lib/protos/peer/events.proto b/hfc/lib/protos/peer/events.proto index 9ce2ec59ea..29273e5100 100644 --- a/hfc/lib/protos/peer/events.proto +++ b/hfc/lib/protos/peer/events.proto @@ -17,7 +17,8 @@ limitations under the License. syntax = "proto3"; import "chaincodeevent.proto"; -import "fabric.proto"; +import "fabric_transaction.proto"; +import "fabric_block.proto"; option go_package = "github.com/hyperledger/fabric/protos/peer"; @@ -81,7 +82,7 @@ message Event { Register register = 1; //producer events - Block block = 2; + Block2 block = 2; ChaincodeEvent chaincodeEvent = 3; Rejection rejection = 4; diff --git a/hfc/lib/protos/peer/fabric.proto b/hfc/lib/protos/peer/fabric.proto index 528709a847..0d7ac13fdd 100644 --- a/hfc/lib/protos/peer/fabric.proto +++ b/hfc/lib/protos/peer/fabric.proto @@ -18,118 +18,6 @@ syntax = "proto3"; option go_package = "github.com/hyperledger/fabric/protos/peer"; package protos; -import "chaincode.proto"; -import "chaincodeevent.proto"; -import "../google/protobuf/timestamp.proto"; - - -// Transaction defines a function call to a contract. -// `args` is an array of type string so that the chaincode writer can choose -// whatever format they wish for the arguments for their chaincode. -// For example, they may wish to use JSON, XML, or a custom format. -// TODO: Defined remaining fields. -message Transaction { - enum Type { - UNDEFINED = 0; - // deploy a chaincode to the network and call `Init` function - CHAINCODE_DEPLOY = 1; - // call a chaincode `Invoke` function as a transaction - CHAINCODE_INVOKE = 2; - // call a chaincode `query` function - CHAINCODE_QUERY = 3; - // terminate a chaincode; not implemented yet - CHAINCODE_TERMINATE = 4; - } - Type type = 1; - //store ChaincodeID as bytes so its encrypted value can be stored - bytes chaincodeID = 2; - bytes payload = 3; - bytes metadata = 4; - string txid = 5; - google.protobuf.Timestamp timestamp = 6; - - ConfidentialityLevel confidentialityLevel = 7; - string confidentialityProtocolVersion = 8; - bytes nonce = 9; - - bytes toValidators = 10; - bytes cert = 11; - bytes signature = 12; -} - -// TransactionBlock carries a batch of transactions. -message TransactionBlock { - repeated Transaction transactions = 1; -} - -// TransactionResult contains the return value of a transaction. It does -// not track potential state changes that were a result of the transaction. -// txid - The unique identifier of this transaction. -// result - The return value of the transaction. -// errorCode - An error code. 5xx will be logged as a failure in the dashboard. -// error - An error string for logging an issue. -// chaincodeEvent - any event emitted by a transaction -message TransactionResult { - string txid = 1; - bytes result = 2; - uint32 errorCode = 3; - string error = 4; - ChaincodeEvent chaincodeEvent = 5; -} - -// Block carries The data that describes a block in the blockchain. -// version - Version used to track any protocol changes. -// timestamp - The time at which the block or transaction order -// was proposed. This may not be used by all consensus modules. -// transactions - The ordered list of transactions in the block. -// stateHash - The state hash after running transactions in this block. -// previousBlockHash - The hash of the previous block in the chain. -// consensusMetadata - Consensus modules may optionally store any -// additional metadata in this field. -// nonHashData - Data stored with the block, but not included in the blocks -// hash. This allows this data to be different per peer or discarded without -// impacting the blockchain. -message Block { - uint32 version = 1; - google.protobuf.Timestamp timestamp = 2; - repeated Transaction transactions = 3; - bytes stateHash = 4; - bytes previousBlockHash = 5; - bytes consensusMetadata = 6; - NonHashData nonHashData = 7; -} - -// Contains information about the blockchain ledger such as height, current -// block hash, and previous block hash. -message BlockchainInfo { - - uint64 height = 1; - bytes currentBlockHash = 2; - bytes previousBlockHash = 3; - -} - -// NonHashData is data that is recorded on the block, but not included in -// the block hash when verifying the blockchain. -// localLedgerCommitTimestamp - The time at which the block was added -// to the ledger on the local peer. -// chaincodeEvent - is an array ChaincodeEvents, one per transaction in the -// block -message NonHashData { - google.protobuf.Timestamp localLedgerCommitTimestamp = 1; - repeated ChaincodeEvent chaincodeEvents = 2; -} - -// Interface exported by the server. -service Peer { - // Accepts a stream of Message during chat session, while receiving - // other Message (e.g. from other peers). - rpc Chat(stream Message) returns (stream Message) {} - - // Process a transaction from a remote source. - rpc ProcessTransaction(Transaction) returns (Response) {} - -} message PeerAddress { string host = 1; @@ -160,106 +48,12 @@ message PeersAddresses { repeated string addresses = 1; } -message HelloMessage { - PeerEndpoint peerEndpoint = 1; - BlockchainInfo blockchainInfo = 2; -} - -message Message { - enum Type { - UNDEFINED = 0; - - DISC_HELLO = 1; - DISC_DISCONNECT = 2; - DISC_GET_PEERS = 3; - DISC_PEERS = 4; - DISC_NEWMSG = 5; - - CHAIN_TRANSACTION = 6; - - SYNC_GET_BLOCKS = 11; - SYNC_BLOCKS = 12; - SYNC_BLOCK_ADDED = 13; - - SYNC_STATE_GET_SNAPSHOT = 14; - SYNC_STATE_SNAPSHOT = 15; - SYNC_STATE_GET_DELTAS = 16; - SYNC_STATE_DELTAS = 17; - - RESPONSE = 20; - CONSENSUS = 21; - } - Type type = 1; - google.protobuf.Timestamp timestamp = 2; - bytes payload = 3; - bytes signature = 4; -} - -message Response { - enum StatusCode { - UNDEFINED = 0; - SUCCESS = 200; - FAILURE = 500; - } - StatusCode status = 1; - bytes msg = 2; -} - -// BlockState is the payload of Message.SYNC_BLOCK_ADDED. When a VP -// commits a new block to the ledger, it will notify its connected NVPs of the -// block and the delta state. The NVP may call the ledger APIs to apply the -// block and the delta state to its ledger if the block's previousBlockHash -// equals to the NVP's current block hash -message BlockState { - Block block = 1; - bytes stateDelta = 2; -} - -// SyncBlockRange is the payload of Message.SYNC_GET_BLOCKS, where -// start and end indicate the starting and ending blocks inclusively. The order -// in which blocks are returned is defined by the start and end values. For -// example, if start=3 and end=5, the order of blocks will be 3, 4, 5. -// If start=5 and end=3, the order will be 5, 4, 3. -message SyncBlockRange { - uint64 correlationId = 1; - uint64 start = 2; - uint64 end = 3; -} - -// SyncBlocks is the payload of Message.SYNC_BLOCKS, where the range -// indicates the blocks responded to the request SYNC_GET_BLOCKS -message SyncBlocks { - SyncBlockRange range = 1; - repeated Block blocks = 2; -} - -// SyncSnapshotRequest Payload for the penchainMessage.SYNC_GET_SNAPSHOT message. -message SyncStateSnapshotRequest { - uint64 correlationId = 1; -} - -// SyncStateSnapshot is the payload of Message.SYNC_SNAPSHOT, which is a response -// to penchainMessage.SYNC_GET_SNAPSHOT. It contains the snapshot or a chunk of the -// snapshot on stream, and in which case, the sequence indicate the order -// starting at 0. The terminating message will have len(delta) == 0. -message SyncStateSnapshot { - bytes delta = 1; - uint64 sequence = 2; - uint64 blockNumber = 3; - SyncStateSnapshotRequest request = 4; -} +// Contains information about the blockchain ledger such as height, current +// block hash, and previous block hash. +message BlockchainInfo { -// SyncStateDeltasRequest is the payload of Message.SYNC_GET_STATE. -// blockNumber indicates the block number for the delta which is being -// requested. If no payload is included with SYNC_GET_STATE, it represents -// a request for a snapshot of the current state. -message SyncStateDeltasRequest { - SyncBlockRange range = 1; -} + uint64 height = 1; + bytes currentBlockHash = 2; + bytes previousBlockHash = 3; -// SyncStateDeltas is the payload of the Message.SYNC_STATE in response to -// the Message.SYNC_GET_STATE message. -message SyncStateDeltas { - SyncBlockRange range = 1; - repeated bytes deltas = 2; } diff --git a/hfc/lib/protos/peer/fabric_message.proto b/hfc/lib/protos/peer/fabric_message.proto index 2b12d4935e..9bbcfe6292 100644 --- a/hfc/lib/protos/peer/fabric_message.proto +++ b/hfc/lib/protos/peer/fabric_message.proto @@ -20,8 +20,8 @@ option go_package = "github.com/hyperledger/fabric/protos/peer"; package protos; -// A Message2 encapsulates a payload of the indicated type in this message. -message Message2 { +// A Message encapsulates a payload of the indicated type in this message. +message Message { enum Type { diff --git a/hfc/lib/protos/peer/fabric_proposal_response.proto b/hfc/lib/protos/peer/fabric_proposal_response.proto index 3ba30f9a2b..1353e64dfa 100644 --- a/hfc/lib/protos/peer/fabric_proposal_response.proto +++ b/hfc/lib/protos/peer/fabric_proposal_response.proto @@ -41,7 +41,7 @@ message ProposalResponse { // A response message indicating whether the // endorsement of the action was successful - Response2 response = 4; + Response response = 4; // The payload of response. It is the bytes of ProposalResponsePayload bytes payload = 5; @@ -53,7 +53,7 @@ message ProposalResponse { // A response with a representation similar to an HTTP response that can // be used within another message. -message Response2 { +message Response { // A status code that should follow the HTTP status codes. int32 status = 1; diff --git a/hfc/lib/protos/peer/fabric_transaction.proto b/hfc/lib/protos/peer/fabric_transaction.proto index 75d3bacc78..a2621831eb 100644 --- a/hfc/lib/protos/peer/fabric_transaction.proto +++ b/hfc/lib/protos/peer/fabric_transaction.proto @@ -43,7 +43,7 @@ message InvalidTransaction { TxIdAlreadyExists = 0; RWConflictDuringCommit = 1; } - Transaction2 transaction = 1; + Transaction transaction = 1; Cause cause = 2; } @@ -59,7 +59,7 @@ message InvalidTransaction { // (ProposalResponsePayload) with one signature per Endorser. Any number of // independent proposals (and their action) might be included in a transaction // to ensure that they are treated atomically. -message Transaction2 { +message Transaction { // Version indicates message protocol version. int32 version = 1; diff --git a/hfc/lib/protos/peer/fabric_transaction_header.proto b/hfc/lib/protos/peer/fabric_transaction_header.proto deleted file mode 100644 index 4a1b30435a..0000000000 --- a/hfc/lib/protos/peer/fabric_transaction_header.proto +++ /dev/null @@ -1,71 +0,0 @@ -/* -Copyright IBM Corp. 2016 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. -*/ - -syntax = "proto3"; - -option go_package = "github.com/hyperledger/fabric/protos/peer"; - -package protos; - -import "../google/protobuf/timestamp.proto"; - -// A Header contains fields that are common to all proposals and all -// transactions, no matter their type. It can include also type-dependant -// fields by using the 'extensions' field. This header is on purpose the same -// header for proposals (a request to do "something" on the ledger) and a -// transaction (the endorsed actions following from some request). -// Furthermore, a proposal, its endorsements and the resulting transaction are -// linked together by this message, as follows -// 1. a Proposal contains a Header -// 2. the hash of the Header of a proposal is included in the proposal response -// generated by each endorser as a result of that proposal -// 3. a TransactionAction contains both i) the *same* Header (byte-by-byte) of -// the corresponsing Proposal and ii) the hash of the Header in each of the -// endorsed actions -message Header { - - enum Type { - UNDEFINED = 0; - CHAINCODE = 1; - } - - // Version indicates message protocol version - int32 version = 1; - - // Timestamp is the local time when the message was created - // by the sender - google.protobuf.Timestamp timestamp = 2; - - // Type of the transaction - Type type = 3; - - // Creator of the header (and encapsulating message). This is usually a tcert - // or ecert identifying the entity who submits the proposal/transaction. The - // creator identifies the signer of - // 1. a proposal (if this is the header of a Proposal message) - // 2. a transaction (if this is the header of a TransactionAction message) - bytes creator = 4; - - // Arbitrary number that may only be used once. This ensures the hash of - // the proposal is unique and may be used in replay detection - bytes nonce = 5; - - // Identifier of the chain this header targets to - bytes chainID = 6; - - // Extensions is used to include type-dependant fields - bytes extensions = 7; -} diff --git a/hfc/lib/utils.js b/hfc/lib/utils.js index b74a96aa93..816dbafb76 100644 --- a/hfc/lib/utils.js +++ b/hfc/lib/utils.js @@ -24,6 +24,7 @@ var zlib = require('zlib'); var urlParser = require('url'); var winston = require('winston'); var Config = require('./Config.js'); +var crypto = require('crypto'); // // Load required crypto stuff. @@ -366,3 +367,32 @@ module.exports.existsSync = function(absolutePath /*string*/) { } }; +// utility function to build an unique transaction id +// The request object may contain values that could be +// used to help generate the result value +module.exports.buildTransactionID = function(request /*object*/) { + var length = 10; + if(request && request.length) { + length = request.length; + } + var value = crypto.randomBytes(10); //TODO how should we really generate this value + return value; +}; + +// utility function to create a random number of +// the specified length. +module.exports.getNonce = function(length) { + if(length) { + if(Number.isInteger(length)) { + // good, it is a number + } else { + throw new Error('Parameter must be an integer'); + } + } else { + length = this.getConfigSetting('nonce-size', 24); + } + + var value = crypto.randomBytes(length); + return value; +}; + diff --git a/test/fixtures/docker-compose.yml b/test/fixtures/docker-compose.yml index 3f68b91fc2..86e5cf8327 100644 --- a/test/fixtures/docker-compose.yml +++ b/test/fixtures/docker-compose.yml @@ -33,24 +33,24 @@ vp0: ports: - 7051:7051 -vp1: - image: hyperledger/fabric-peer - environment: - - CORE_PEER_ADDRESSAUTODETECT=true - - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock - - CORE_LOGGING_LEVEL=DEBUG - - CORE_PEER_NETWORKID=${CORE_PEER_NETWORKID} - - CORE_NEXT=true - - CORE_PEER_ENDORSER_ENABLED=true - - CORE_PEER_ID=vp1 - - CORE_PEER_PROFILE_ENABLED=true - - CORE_PEER_COMMITTER_LEDGER_ORDERER=orderer:7050 - - CORE_PEER_DISCOVERY_ROOTNODE=vp0:7051 - volumes: - - /var/run/:/host/var/run/ - command: peer node start - links: - - orderer - - vp0 - ports: - - 7056:7051 +# vp1: +# image: hyperledger/fabric-peer +# environment: +# - CORE_PEER_ADDRESSAUTODETECT=true +# - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock +# - CORE_LOGGING_LEVEL=DEBUG +# - CORE_PEER_NETWORKID=${CORE_PEER_NETWORKID} +# - CORE_NEXT=true +# - CORE_PEER_ENDORSER_ENABLED=true +# - CORE_PEER_ID=vp1 +# - CORE_PEER_PROFILE_ENABLED=true +# - CORE_PEER_COMMITTER_LEDGER_ORDERER=orderer:7050 +# - CORE_PEER_DISCOVERY_ROOTNODE=vp0:7051 +# volumes: +# - /var/run/:/host/var/run/ +# command: peer node start +# links: +# - orderer +# - vp0 +# ports: +# - 7056:7051 diff --git a/test/unit/end-to-end.js b/test/unit/end-to-end.js index a04e088902..f91736bd42 100644 --- a/test/unit/end-to-end.js +++ b/test/unit/end-to-end.js @@ -31,7 +31,10 @@ var utils = require('hfc/lib/utils.js'); var chain = hfc.newChain('testChain-e2e'); var webUser; -var chaincode_id = 'mycc1'; +var chaincode_id = 'mycc2'; +var chain_id = '**TEST_CHAINID**'; +var tx_id = null; +var nonce = null; testUtil.setupChaincodeDeploy(); @@ -52,14 +55,19 @@ test('End-to-end flow of chaincode deploy, transaction invocation, and query', f function(admin) { t.pass('Successfully enrolled user \'admin\''); webUser = admin; + tx_id = hfc.buildTransactionID({length:12}); + nonce = hfc.getNonce(); // send proposal to endorser var request = { - targets: [hfc.getPeer('grpc://localhost:7051'), hfc.getPeer('grpc://localhost:7056')], + targets: [hfc.getPeer('grpc://localhost:7051')], // hfc.getPeer('grpc://localhost:7056')], chaincodePath: testUtil.CHAINCODE_PATH, chaincodeId: chaincode_id, fcn: 'init', args: ['a', '100', 'b', '200'], + chainId: chain_id, + txId: tx_id, + nonce: nonce, 'dockerfile-contents' : 'from hyperledger/fabric-ccenv\n' + 'COPY . $GOPATH/src/build-chaincode/\n' + @@ -78,9 +86,15 @@ test('End-to-end flow of chaincode deploy, transaction invocation, and query', f var proposalResponses = results[0]; //console.log('proposalResponses:'+JSON.stringify(proposalResponses)); var proposal = results[1]; + var header = results[2]; if (proposalResponses && proposalResponses[0].response && proposalResponses[0].response.status === 200) { t.pass(util.format('Successfully sent Proposal and received ProposalResponse: Status - %s, message - "%s", metadata - "%s", endorsement signature: %s', proposalResponses[0].response.status, proposalResponses[0].response.message, proposalResponses[0].response.payload, proposalResponses[0].endorsement.signature)); - return webUser.sendTransaction(proposalResponses, proposal); + var request = { + proposalResponses: proposalResponses, + proposal: proposal, + header: header + }; + return webUser.sendTransaction(request); } else { t.fail('Failed to send Proposal or receive valid response. Response null or status is not 200. exiting...'); t.end(); @@ -108,12 +122,17 @@ test('End-to-end flow of chaincode deploy, transaction invocation, and query', f } ).then( function() { + tx_id = hfc.buildTransactionID({length:12}); + nonce = hfc.getNonce(); // send proposal to endorser var request = { - targets: [hfc.getPeer('grpc://localhost:7051'), hfc.getPeer('grpc://localhost:7056')], + targets: [hfc.getPeer('grpc://localhost:7051')], // hfc.getPeer('grpc://localhost:7056')], chaincodeId : chaincode_id, fcn: 'invoke', - args: ['move', 'a', 'b','100'] + args: ['move', 'a', 'b','100'], + chainId: chain_id, + txId: tx_id, + nonce: nonce }; return webUser.sendTransactionProposal(request); }, @@ -125,9 +144,15 @@ test('End-to-end flow of chaincode deploy, transaction invocation, and query', f function(results) { var proposalResponses = results[0]; var proposal = results[1]; + var header = results[2]; if (proposalResponses[0].response.status === 200) { - t.pass('Successfully obtained transaction endorsement.' + JSON.stringify(proposalResponses)); - return webUser.sendTransaction(proposalResponses, proposal); + t.pass('Successfully obtained transaction endorsement.'); // + JSON.stringify(proposalResponses)); + var request = { + proposalResponses: proposalResponses, + proposal: proposal, + header: header + }; + return webUser.sendTransaction(request); } else { t.fail('Failed to obtain transaction endorsement. Error code: ' + status); t.end(); @@ -156,8 +181,11 @@ test('End-to-end flow of chaincode deploy, transaction invocation, and query', f function() { // send query var request = { - targets: [hfc.getPeer('grpc://localhost:7051'), hfc.getPeer('grpc://localhost:7056')], + targets: [hfc.getPeer('grpc://localhost:7051')], // hfc.getPeer('grpc://localhost:7056')], chaincodeId : chaincode_id, + chainId: chain_id, + txId: hfc.buildTransactionID(), + nonce: hfc.getNonce(), fcn: 'invoke', args: ['query','b'] }; diff --git a/test/unit/headless-tests.js b/test/unit/headless-tests.js index 44e637ac05..b533bbc441 100644 --- a/test/unit/headless-tests.js +++ b/test/unit/headless-tests.js @@ -567,13 +567,17 @@ test('\n\n ** Member sendDeploymentProposal() tests **\n\n', function (t) { var m = new Member('does not matter', _chain); var p1 = m.sendDeploymentProposal({ - chaincodePath: 'blah', + targets: [hfc.getPeer('grpc://localhost:7051')], chaincodeId: 'blah', - fcn: 'init' + fcn: 'init', + args: ['a', '100', 'b', '200'], + chainId: 'blah', + txId: 'blah', + nonce: 'blah' }).then(function () { t.fail('Should not have been able to resolve the promise because of missing "peer" parameter'); }).catch(function (err) { - if (err.message === 'Missing "targets" for the endorsing peer objects in the Deployment proposal request') { + if (err.message.indexOf('Missing chaincodePath parameter in Deployment proposal request') >= 0) { t.pass('Successfully caught missing peer error'); } else { t.fail('Failed to catch the missing peer error. Error: ' + err.stack ? err.stask : err); @@ -581,32 +585,106 @@ test('\n\n ** Member sendDeploymentProposal() tests **\n\n', function (t) { }); var p2 = m.sendDeploymentProposal({ - endorserUrl: 'blah', - fcn: 'init' + targets: [hfc.getPeer('grpc://localhost:7051')], + chaincodePath: 'blah', + chaincodeId: 'blah', + fcn: 'init', + args: ['a', '100', 'b', '200'], + txId: 'blah', + nonce: 'blah' }).then(function () { - t.fail('Should not have been able to resolve the promise because of missing "chaincodePath" parameter'); + t.fail('Should not have been able to resolve the promise because of missing "chainId" parameter'); }).catch(function (err) { - if (err.message === 'Missing chaincodePath in Deployment proposal request') { - t.pass('Successfully caught missing chaincodePath error'); + if (err.message.indexOf('Missing "chainId" parameter in the proposal request') >= 0) { + t.pass('Successfully caught missing chainId error'); } else { - t.fail('Failed to catch the missing chaincodePath error. Error: ' + err.stack ? err.stask : err); + t.fail('Failed to catch the missing chainId error. Error: ' + err.stack ? err.stask : err); } }); var p3 = m.sendDeploymentProposal({ + targets: [hfc.getPeer('grpc://localhost:7051')], chaincodePath: 'blah', - fcn: 'init' + chainId: 'blah', + fcn: 'init', + args: ['a', '100', 'b', '200'], + txId: 'blah', + nonce: 'blah' }).then(function () { t.fail('Should not have been able to resolve the promise because of missing "chaincodeId" parameter'); }).catch(function (err) { - if (err.message === 'Missing chaincodeId in the Deployment proposal request') { + if (err.message.indexOf('Missing "chaincodeId" parameter in the proposal request') >= 0) { t.pass('Successfully caught missing chaincodeId error'); } else { t.fail('Failed to catch the missing chaincodeId error. Error: ' + err.stack ? err.stask : err); } }); - Promise.all([p1, p2, p3]) + var p4 = m.sendDeploymentProposal({ + chaincodePath: 'blah', + chaincodeId: 'blah', + chainId: 'blah', + fcn: 'init', + args: ['a', '100', 'b', '200'], + txId: 'blah', + nonce: 'blah' + }).then(function () { + t.fail('Should not have been able to resolve the promise because of missing "targets" parameter'); + }).catch(function (err) { + if (err.message.indexOf('Missing "targets" parameter in the proposal request') >= 0) { + t.pass('Successfully caught missing targets error'); + } else { + t.fail('Failed to catch the missing targets error. Error: ' + err.stack ? err.stask : err); + } + }); + + var p5 = m.sendDeploymentProposal({ + targets: [hfc.getPeer('grpc://localhost:7051')], + chaincodePath: 'blah', + chaincodeId: 'blah', + chainId: 'blah', + fcn: 'init', + args: ['a', '100', 'b', '200'], + nonce: 'blah' + }).then(function () { + t.fail('Should not have been able to resolve the promise because of missing "txId" parameter'); + }).catch(function (err) { + if (err.message.indexOf('Missing "txId" parameter in the proposal request') >= 0) { + t.pass('Successfully caught missing txId error'); + } else { + t.fail('Failed to catch the missing txId error. Error: ' + err.stack ? err.stask : err); + } + }); + + var p6 = m.sendDeploymentProposal({ + targets: [hfc.getPeer('grpc://localhost:7051')], + chaincodePath: 'blah', + chaincodeId: 'blah', + chainId: 'blah', + fcn: 'init', + args: ['a', '100', 'b', '200'], + txId: 'blah' + }).then(function () { + t.fail('Should not have been able to resolve the promise because of missing "nonce" parameter'); + }).catch(function (err) { + if (err.message.indexOf('Missing "nonce" parameter in the proposal request') >= 0) { + t.pass('Successfully caught missing nonce error'); + } else { + t.fail('Failed to catch the missing nonce error. Error: ' + err.stack ? err.stask : err); + } + }); + + var p7 = m.sendDeploymentProposal().then(function () { + t.fail('Should not have been able to resolve the promise because of missing request parameter'); + }).catch(function (err) { + if (err.message.indexOf('Missing input request object on the proposal request') >= 0) { + t.pass('Successfully caught missing request error'); + } else { + t.fail('Failed to catch the missing request error. Error: ' + err.stack ? err.stask : err); + } + }); + + Promise.all([p1, p2, p3, p4, p6, p7]) .then( function (data) { t.end(); @@ -623,61 +701,118 @@ test('\n\n ** Member sendTransactionProposal() tests **\n\n', function (t) { var m = new Member('does not matter', _chain); var p1 = m.sendTransactionProposal({ - chaincodeId: 'someid' + targets: [hfc.getPeer('grpc://localhost:7051')], + chaincodeId : 'blah', + fcn: 'invoke', + chainId: 'blah', + txId: 'blah', + nonce: 'blah' }).then(function () { - t.fail('Should not have been able to resolve the promise because of missing "targets" parameter'); - }, function (err) { - if (err.message === 'Missing "targets" for endorser peer objects in the Transaction proposal request') { + t.fail('Should not have been able to resolve the promise because of missing "args" parameter'); + }).catch(function (err) { + if (err.message.indexOf('Missing "targets" for endorser peer objects in the Transaction proposal request')) { t.pass('Successfully caught missing targets error'); } else { t.fail('Failed to catch the missing targets error. Error: ' + err.stack ? err.stask : err); } + }); + + var p2 = m.sendTransactionProposal({ + targets: [hfc.getPeer('grpc://localhost:7051')], + chaincodeId: 'blah', + fcn: 'init', + args: ['a', '100', 'b', '200'], + txId: 'blah', + nonce: 'blah' + }).then(function () { + t.fail('Should not have been able to resolve the promise because of missing "chainId" parameter'); }).catch(function (err) { - if (err.message === 'Missing "targets" for endorser peer objects in the Transaction proposal request') { - t.pass('Successfully caught missing targets error'); + if (err.message.indexOf('Missing "chainId" parameter in the proposal request') >= 0) { + t.pass('Successfully caught missing chainId error'); } else { - t.fail('Failed to catch the missing targets error. Error: ' + err.stack ? err.stask : err); + t.fail('Failed to catch the missing chainId error. Error: ' + err.stack ? err.stask : err); } }); - var p2 = m.sendTransactionProposal({ - targets: [hfc.getPeer('grpc://somehost.com:9000')] + var p3 = m.sendTransactionProposal({ + targets: [hfc.getPeer('grpc://localhost:7051')], + chainId: 'blah', + fcn: 'init', + args: ['a', '100', 'b', '200'], + txId: 'blah', + nonce: 'blah' }).then(function () { - t.fail('Should not have been able to resolve the promise because of missing "chaincodePath" parameter'); - }, function (err) { - if (err.message === 'Missing chaincodeId in the Transaction proposal request') { - t.pass('Successfully caught missing chaincodeid error'); + t.fail('Should not have been able to resolve the promise because of missing "chaincodeId" parameter'); + }).catch(function (err) { + if (err.message.indexOf('Missing "chaincodeId" parameter in the proposal request') >= 0) { + t.pass('Successfully caught missing chaincodeId error'); } else { - t.fail('Failed to catch the missing chaincodeid error. Error: ' + err.stack ? err.stask : err); + t.fail('Failed to catch the missing chaincodeId error. Error: ' + err.stack ? err.stask : err); } + }); + + var p4 = m.sendTransactionProposal({ + chaincodeId: 'blah', + chainId: 'blah', + fcn: 'init', + args: ['a', '100', 'b', '200'], + txId: 'blah', + nonce: 'blah' + }).then(function () { + t.fail('Should not have been able to resolve the promise because of missing "targets" parameter'); }).catch(function (err) { - if (err.message === 'Missing chaincode ID in the Transaction proposal request') { - t.pass('Successfully caught missing chaincodeid error'); + if (err.message.indexOf('Missing "targets" parameter in the proposal request') >= 0) { + t.pass('Successfully caught missing targets error'); } else { - t.fail('Failed to catch the missing chaincodeid error. Error: ' + err.stack ? err.stask : err); + t.fail('Failed to catch the missing targets error. Error: ' + err.stack ? err.stask : err); } }); - var p3 = m.sendTransactionProposal({ - targets: [hfc.getPeer('grpc://somehost.com:9000')], - chaincodeId: 'someid' + var p5 = m.sendTransactionProposal({ + targets: [hfc.getPeer('grpc://localhost:7051')], + chaincodeId: 'blah', + chainId: 'blah', + fcn: 'init', + args: ['a', '100', 'b', '200'], + nonce: 'blah' }).then(function () { - t.fail('Should not have been able to resolve the promise because of missing "chaincodePath" parameter'); - }, function (err) { - if (err.message === 'Missing arguments in Transaction proposal request') { - t.pass('Successfully caught missing args error'); + t.fail('Should not have been able to resolve the promise because of missing "txId" parameter'); + }).catch(function (err) { + if (err.message.indexOf('Missing "txId" parameter in the proposal request') >= 0) { + t.pass('Successfully caught missing txId error'); } else { - t.fail('Failed to catch the missing args error. Error: ' + err.stack ? err.stask : err); + t.fail('Failed to catch the missing txId error. Error: ' + err.stack ? err.stask : err); } + }); + + var p6 = m.sendTransactionProposal({ + targets: [hfc.getPeer('grpc://localhost:7051')], + chaincodeId: 'blah', + chainId: 'blah', + fcn: 'init', + args: ['a', '100', 'b', '200'], + txId: 'blah' + }).then(function () { + t.fail('Should not have been able to resolve the promise because of missing "nonce" parameter'); + }).catch(function (err) { + if (err.message.indexOf('Missing "nonce" parameter in the proposal request') >= 0) { + t.pass('Successfully caught missing nonce error'); + } else { + t.fail('Failed to catch the missing nonce error. Error: ' + err.stack ? err.stask : err); + } + }); + + var p7 = m.sendTransactionProposal().then(function () { + t.fail('Should not have been able to resolve the promise because of missing request parameter'); }).catch(function (err) { - if (err.message === 'Missing arguments in Transaction proposal request') { - t.pass('Successfully caught missing args error'); + if (err.message.indexOf('Missing input request object on the proposal request') >= 0) { + t.pass('Successfully caught missing request error'); } else { - t.fail('Failed to catch the missing args error. Error: ' + err.stack ? err.stask : err); + t.fail('Failed to catch the missing request error. Error: ' + err.stack ? err.stask : err); } }); - Promise.all([p1, p2, p3]) + Promise.all([p1, p2, p3, p4, p5, p6, p7]) .then( function (data) { t.end(); @@ -694,61 +829,118 @@ test('\n\n ** Member queryByChaincode() tests **\n\n', function (t) { var m = new Member('does not matter', _chain); var p1 = m.queryByChaincode({ - chaincodeId: 'someid' + targets: [hfc.getPeer('grpc://localhost:7051')], + chaincodeId : 'blah', + fcn: 'invoke', + chainId: 'blah', + txId: 'blah', + nonce: 'blah' }).then(function () { - t.fail('Should not have been able to resolve the promise because of missing "targets" parameter'); - }, function (err) { - if (err.message === 'Missing "targets" for endorser peer objects in the Transaction proposal request') { + t.fail('Should not have been able to resolve the promise because of missing "args" parameter'); + }).catch(function (err) { + if (err.message.indexOf('Missing "targets" for endorser peer objects in the Transaction proposal request')) { t.pass('Successfully caught missing targets error'); } else { t.fail('Failed to catch the missing targets error. Error: ' + err.stack ? err.stask : err); } + }); + + var p2 = m.queryByChaincode({ + targets: [hfc.getPeer('grpc://localhost:7051')], + chaincodeId: 'blah', + fcn: 'init', + args: ['a', '100', 'b', '200'], + txId: 'blah', + nonce: 'blah' + }).then(function () { + t.fail('Should not have been able to resolve the promise because of missing "chainId" parameter'); }).catch(function (err) { - if (err.message === 'Missing "targets" for endorser peer objects in the Transaction proposal request') { - t.pass('Successfully caught missing targets error'); + if (err.message.indexOf('Missing "chainId" parameter in the proposal request') >= 0) { + t.pass('Successfully caught missing chainId error'); } else { - t.fail('Failed to catch the missing targets error. Error: ' + err.stack ? err.stask : err); + t.fail('Failed to catch the missing chainId error. Error: ' + err.stack ? err.stask : err); } }); - var p2 = m.queryByChaincode({ - targets: [hfc.getPeer('grpc://somehost.com:9000')] + var p3 = m.queryByChaincode({ + targets: [hfc.getPeer('grpc://localhost:7051')], + chainId: 'blah', + fcn: 'init', + args: ['a', '100', 'b', '200'], + txId: 'blah', + nonce: 'blah' }).then(function () { - t.fail('Should not have been able to resolve the promise because of missing "chaincodePath" parameter'); - }, function (err) { - if (err.message === 'Missing chaincodeId in the Transaction proposal request') { - t.pass('Successfully caught missing chaincodeid error'); + t.fail('Should not have been able to resolve the promise because of missing "chaincodeId" parameter'); + }).catch(function (err) { + if (err.message.indexOf('Missing "chaincodeId" parameter in the proposal request') >= 0) { + t.pass('Successfully caught missing chaincodeId error'); } else { - t.fail('Failed to catch the missing chaincodeid error. Error: ' + err.stack ? err.stask : err); + t.fail('Failed to catch the missing chaincodeId error. Error: ' + err.stack ? err.stask : err); } + }); + + var p4 = m.queryByChaincode({ + chaincodeId: 'blah', + chainId: 'blah', + fcn: 'init', + args: ['a', '100', 'b', '200'], + txId: 'blah', + nonce: 'blah' + }).then(function () { + t.fail('Should not have been able to resolve the promise because of missing "targets" parameter'); }).catch(function (err) { - if (err.message === 'Missing chaincode ID in the Transaction proposal request') { - t.pass('Successfully caught missing chaincodeid error'); + if (err.message.indexOf('Missing "targets" parameter in the proposal request') >= 0) { + t.pass('Successfully caught missing targets error'); } else { - t.fail('Failed to catch the missing chaincodeid error. Error: ' + err.stack ? err.stask : err); + t.fail('Failed to catch the missing targets error. Error: ' + err.stack ? err.stask : err); } }); - var p3 = m.queryByChaincode({ - targets: [hfc.getPeer('grpc://somehost.com:9000')], - chaincodeId: 'someid' + var p5 = m.queryByChaincode({ + targets: [hfc.getPeer('grpc://localhost:7051')], + chaincodeId: 'blah', + chainId: 'blah', + fcn: 'init', + args: ['a', '100', 'b', '200'], + nonce: 'blah' }).then(function () { - t.fail('Should not have been able to resolve the promise because of missing "chaincodePath" parameter'); - }, function (err) { - if (err.message === 'Missing arguments in Transaction proposal request') { - t.pass('Successfully caught missing args error'); + t.fail('Should not have been able to resolve the promise because of missing "txId" parameter'); + }).catch(function (err) { + if (err.message.indexOf('Missing "txId" parameter in the proposal request') >= 0) { + t.pass('Successfully caught missing txId error'); } else { - t.fail('Failed to catch the missing args error. Error: ' + err.stack ? err.stask : err); + t.fail('Failed to catch the missing txId error. Error: ' + err.stack ? err.stask : err); } + }); + + var p6 = m.queryByChaincode({ + targets: [hfc.getPeer('grpc://localhost:7051')], + chaincodeId: 'blah', + chainId: 'blah', + fcn: 'init', + args: ['a', '100', 'b', '200'], + txId: 'blah' + }).then(function () { + t.fail('Should not have been able to resolve the promise because of missing "nonce" parameter'); }).catch(function (err) { - if (err.message === 'Missing arguments in Transaction proposal request') { - t.pass('Successfully caught missing args error'); + if (err.message.indexOf('Missing "nonce" parameter in the proposal request') >= 0) { + t.pass('Successfully caught missing nonce error'); } else { - t.fail('Failed to catch the missing args error. Error: ' + err.stack ? err.stask : err); + t.fail('Failed to catch the missing nonce error. Error: ' + err.stack ? err.stask : err); } }); - Promise.all([p1, p2, p3]) + var p7 = m.queryByChaincode().then(function () { + t.fail('Should not have been able to resolve the promise because of missing request parameter'); + }).catch(function (err) { + if (err.message.indexOf('Missing input request object on the proposal request') >= 0) { + t.pass('Successfully caught missing request error'); + } else { + t.fail('Failed to catch the missing request error. Error: ' + err.stack ? err.stask : err); + } + }); + + Promise.all([p1, p2, p3, p4, p5, p6, p7]) .then( function (data) { t.end(); @@ -768,36 +960,56 @@ test('\n\n ** Member sendTransaction() tests **\n\n', function (t) { .then(function () { t.fail('Should not have been able to resolve the promise because of missing parameters'); }, function (err) { - if (err.message === 'Missing proposalResponse object parameter') { + if (err.message.indexOf('Missing input request object on the proposal request') >= 0) { + t.pass('Successfully caught missing request error'); + } else { + t.fail('Failed to catch the missing request error. Error: ' + err.stack ? err.stask : err); + } + }); + + var p2 = m.sendTransaction({ + proposal: 'blah', + header: '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 "proposalResponse" parameter in transaction request') >= 0) { t.pass('Successfully caught missing proposalResponse error'); } else { - t.fail('Failed to catch the missing object error. Error: ' + err.stack ? err.stask : err); + t.fail('Failed to catch the missing proposalResponse error. Error: ' + err.stack ? err.stask : err); } }); - var p2 = m.sendTransaction('data') + var p3 = m.sendTransaction({ + proposalResponses: 'blah', + header: 'blah' + }) .then(function () { t.fail('Should not have been able to resolve the promise because of missing parameters'); }, function (err) { - if (err.message === 'Missing chaincodeProposal object parameter') { - t.pass('Successfully caught missing chaincodeProposal error'); + if (err.message.indexOf('Missing "proposal" parameter in transaction request') >= 0) { + t.pass('Successfully caught missing proposal error'); } else { - t.fail('Failed to catch the missing objet error. Error: ' + err.stack ? err.stask : err); + t.fail('Failed to catch the missing proposal error. Error: ' + err.stack ? err.stask : err); } }); - var p3 = m.sendTransaction('data', 'data') + var p4 = m.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 === 'no Orderer defined') { - t.pass('Successfully caught missing orderer error'); + 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 order error. Error: ' + err.stack ? err.stask : err); + t.fail('Failed to catch the missing header error. Error: ' + err.stack ? err.stask : err); } }); - Promise.all([p1, p2, p3]) + Promise.all([p1, p2, p3, p4]) .then( function (data) { t.end(); @@ -822,6 +1034,13 @@ var TEST_LONG_MSG = 'The Hyperledger project is an open source collaborative eff 'ensure the transparency, longevity, interoperability and support required to bring blockchain technologies forward to mainstream commercial adoption. That ' + 'is what Hyperledger is about – communities of software developers building blockchain frameworks and platforms.'; +var HASH_MSG_SHA3_384 = '9e9c2e5edf6cbc0b512807a8efa2917daff71b83e04dee28fcc00b1a1dd935fb5afc5eafa06bf55bd64792a597e2a8f3'; +var HASH_LONG_MSG_SHA3_384 = '47a90d6721523682e09b81da0a60e6ee1faf839f0503252316638daf038cf682c0a842edaf310eb0f480a2e181a07af0'; +var HASH_MSG_SHA256 = '4e4aa09b6d80efbd684e80f54a70c1d8605625c3380f4cb012b32644a002b5be'; +var HASH_LONG_MSG_SHA256 = '0d98987f5e4e3ea611f0e3d768c594ff9aac25404265d73554d12c86d7f6fbbc'; +var HASH_MSG_SHA3_256 = '7daeff454f7e91e3cd2d1c1bd5fcd1b6c9d4d5fffc6c327710d8fae7b06ee4a3'; +var HASH_LONG_MSG_SHA3_256 = '577174210438a85ae4311a62e5fccf2441b960013f5691993cdf38ed6ba0c84f'; + var TEST_KEY_PRIVATE = '93f15b31e3c3f3bddcd776d9219e93d8559e31453757b79e193a793cbd239573'; var TEST_KEY_PUBLIC = '04f46815aa00fe2ba2814b906aa4ef1755caf152658de8997a6a858088296054baf45b06b2eba514bcbc37ae0c0cc7465115d36429d0e0bff23dc40e3760c10aa9'; var TEST_MSG_SIGNATURE_SHA2_256 = '3046022100a6460b29373fa16ee96172bfe04666140405fdef78182280545d451f08547736022100d9022fe620ceadabbef1714b894b8d6be4b74c0f9c573bd774871764f4f789c9'; @@ -842,20 +1061,20 @@ test('\n\n ** CryptoSuite_ECDSA_AES - function tests **\n\n', function (t) { t.equal(true, (typeof cryptoUtils._ecdsaCurve !== 'undefined' && typeof cryptoUtils._ecdsa !== 'undefined'), 'CryptoSuite_ECDSA_AES function tests: default instance has "_ecdsaCurve" and "_ecdsa" properties'); - // test default curve 384 with SHA3_384 - t.equal(cryptoUtils.hash(TEST_MSG), '9e9c2e5edf6cbc0b512807a8efa2917daff71b83e04dee28fcc00b1a1dd935fb5afc5eafa06bf55bd64792a597e2a8f3', - 'CryptoSuite_ECDSA_AES function tests: using "SHA3" hashing algorithm with default key size which should be 384'); + // test default curve 256 with SHA256 + t.equal(cryptoUtils.hash(TEST_MSG), HASH_MSG_SHA256, + 'CryptoSuite_ECDSA_AES function tests: using "SHA2" hashing algorithm with default key size which should be 256'); - t.equal(cryptoUtils.hash(TEST_LONG_MSG), '47a90d6721523682e09b81da0a60e6ee1faf839f0503252316638daf038cf682c0a842edaf310eb0f480a2e181a07af0', - 'CryptoSuite_ECDSA_AES function tests: using "SHA3" hashing algorithm with default key size which should be 384'); + t.equal(cryptoUtils.hash(TEST_LONG_MSG), HASH_LONG_MSG_SHA256, + 'CryptoSuite_ECDSA_AES function tests: using "SHA2" hashing algorithm with default key size which should be 256'); cryptoUtils.generateKey() .then(function (key) { - t.equal('secp384r1', key.getPublicKey()._key.curveName, - 'CryptoSuite_ECDSA_AES constructor tests: cryptoUtils generated public key curveName == secp384r1'); + t.equal('secp256r1', key.getPublicKey()._key.curveName, + 'CryptoSuite_ECDSA_AES constructor tests: cryptoUtils generated public key curveName == secp256r1'); // test curve 256 with SHA3_256 - utils.setConfigSetting('crypto-keysize', 256); + utils.setConfigSetting('crypto-hash-algo', 'SHA3'); cryptoUtils = utils.getCryptoSuite(); return cryptoUtils.generateKey(); }) @@ -863,26 +1082,28 @@ test('\n\n ** CryptoSuite_ECDSA_AES - function tests **\n\n', function (t) { t.equal('secp256r1', key.getPublicKey()._key.curveName, 'CryptoSuite_ECDSA_AES constructor tests: ccryptoUtils generated public key curveName == secp256r1'); - t.equal(cryptoUtils.hash(TEST_MSG), '7daeff454f7e91e3cd2d1c1bd5fcd1b6c9d4d5fffc6c327710d8fae7b06ee4a3', + t.equal(cryptoUtils.hash(TEST_MSG), HASH_MSG_SHA3_256, 'CryptoSuite_ECDSA_AES function tests: using "SHA3" hashing algorithm with key size 256'); - t.equal(cryptoUtils.hash(TEST_LONG_MSG), '577174210438a85ae4311a62e5fccf2441b960013f5691993cdf38ed6ba0c84f', + t.equal(cryptoUtils.hash(TEST_LONG_MSG), HASH_LONG_MSG_SHA3_256, 'CryptoSuite_ECDSA_AES function tests: using "SHA3" hashing algorithm with key size 256'); - // test SHA2_256 - utils.setConfigSetting('crypto-hash-algo', 'SHA2'); + // test SHA3_384 + utils.setConfigSetting('crypto-keysize', 384); cryptoUtils = utils.getCryptoSuite(); - t.equal(cryptoUtils.hash(TEST_MSG), '4e4aa09b6d80efbd684e80f54a70c1d8605625c3380f4cb012b32644a002b5be', - 'CryptoSuite_ECDSA_AES function tests: using "SHA2" hashing algorithm with key size 256'); - - t.equal(cryptoUtils.hash(TEST_LONG_MSG), '0d98987f5e4e3ea611f0e3d768c594ff9aac25404265d73554d12c86d7f6fbbc', - 'CryptoSuite_ECDSA_AES function tests: using "SHA2" hashing algorithm with key size 256'); + t.equal(cryptoUtils.hash(TEST_MSG), HASH_MSG_SHA3_384, + 'CryptoSuite_ECDSA_AES function tests: using "SHA2" hashing algorithm with key size 384'); + t.equal(cryptoUtils.hash(TEST_LONG_MSG), HASH_LONG_MSG_SHA3_384, + 'CryptoSuite_ECDSA_AES function tests: using "SHA2" hashing algorithm with key size 384'); return cryptoUtils.generateKey(); }) .then(function (key) { + t.equal('secp384r1', key.getPublicKey()._key.curveName, + 'CryptoSuite_ECDSA_AES constructor tests: ccryptoUtils generated public key curveName == secp384r1'); + if (!!key._key) t.pass('CryptoSuite_ECDSA_AES function tests: verify generateKey return object'); else @@ -890,8 +1111,8 @@ test('\n\n ** CryptoSuite_ECDSA_AES - function tests **\n\n', function (t) { utils.setConfigSetting('crypto-hash-algo', 'sha3'); //lower or upper case is allowed cryptoUtils = utils.getCryptoSuite(); - t.equal(cryptoUtils.hash(TEST_MSG), '7daeff454f7e91e3cd2d1c1bd5fcd1b6c9d4d5fffc6c327710d8fae7b06ee4a3', - 'CryptoSuite_ECDSA_AES function tests: using "SHA3" hashing algorithm with key size 256'); + t.equal(cryptoUtils.hash(TEST_MSG), HASH_MSG_SHA3_384, + 'CryptoSuite_ECDSA_AES function tests: using "SHA3" hashing algorithm with key size 384'); // test generation options return cryptoUtils.generateKey({ ephemeral: true }); diff --git a/test/unit/util.js b/test/unit/util.js index 66afc34ea2..4158132b0f 100644 --- a/test/unit/util.js +++ b/test/unit/util.js @@ -1,4 +1,6 @@ var path = require('path'); +var copService = require('hfc-cop/lib/FabricCOPImpl.js'); +var Member = require('hfc/lib/Member.js'); module.exports.CHAINCODE_PATH = 'github.com/example_cc'; module.exports.CHAINCODE_MARBLES_PATH = 'github.com/marbles_cc';