@@ -22,6 +22,8 @@ const MSPManager = require('./msp/msp-manager.js');
2222const Policy = require ( './Policy.js' ) ;
2323const Constants = require ( './Constants.js' ) ;
2424const CollectionConfig = require ( './SideDB.js' ) ;
25+ const { Identity } = require ( './msp/identity.js' ) ;
26+ const ChannelHelper = require ( './utils/ChannelHelper' ) ;
2527
2628const _ccProto = grpc . load ( __dirname + '/protos/peer/chaincode.proto' ) . protos ;
2729const _transProto = grpc . load ( __dirname + '/protos/peer/transaction.proto' ) . protos ;
@@ -43,7 +45,7 @@ const _discoveryProto = grpc.load(__dirname + '/protos/discovery/protocol.proto'
4345const _gossipProto = grpc . load ( __dirname + '/protos/gossip/message.proto' ) . gossip ;
4446const _collectionProto = grpc . load ( __dirname + '/protos/common/collection.proto' ) . common ;
4547
46- const ImplicitMetaPolicy_Rule = { 0 : 'ANY' , 1 : 'ALL' , 2 : 'MAJORITY' } ;
48+ const ImplicitMetaPolicy_Rule = { 0 : 'ANY' , 1 : 'ALL' , 2 : 'MAJORITY' } ;
4749
4850const PEER_NOT_ASSIGNED_MSG = 'Peer with name "%s" not assigned to this channel' ;
4951const ORDERER_NOT_ASSIGNED_MSG = 'Orderer with name "%s" not assigned to this channel' ;
@@ -92,7 +94,7 @@ const Channel = class {
9294 }
9395 const channelNameRegxChecker = sdk_utils . getConfigSetting ( 'channel-name-regx-checker' ) ;
9496 if ( channelNameRegxChecker ) {
95- const { pattern, flags} = channelNameRegxChecker ;
97+ const { pattern, flags } = channelNameRegxChecker ;
9698 const namePattern = new RegExp ( pattern ? pattern : '' , flags ? flags : '' ) ;
9799 if ( ! ( name . match ( namePattern ) ) ) {
98100 throw new Error ( util . format ( 'Failed to create Channel. channel name should match Regex %s, but got %j' , namePattern , name ) ) ;
@@ -485,7 +487,7 @@ const Channel = class {
485487 const keys = Object . keys ( msps ) ;
486488 for ( const key in keys ) {
487489 const msp = msps [ keys [ key ] ] ;
488- const msp_org = { id : msp . getId ( ) } ;
490+ const msp_org = { id : msp . getId ( ) } ;
489491 logger . debug ( '%s - found %j' , method , msp_org ) ;
490492 orgs . push ( msp_org ) ;
491493 }
@@ -871,7 +873,7 @@ const Channel = class {
871873 seekPayload . setHeader ( seekHeader ) ;
872874 seekPayload . setData ( seekInfo . toBuffer ( ) ) ;
873875 // building manually or will get protobuf errors on send
874- const envelope = client_utils . toEnvelope ( client_utils . signProposal ( signer , seekPayload ) ) ;
876+ const envelope = client_utils . toEnvelope ( client_utils . signProposal ( signer , seekPayload ) ) ;
875877
876878 return orderer . sendDeliver ( envelope ) ;
877879 }
@@ -985,7 +987,7 @@ const Channel = class {
985987 discovery_request . setQueries ( queries ) ;
986988
987989 // build up the outbound request object
988- const signed_request = client_utils . toEnvelope ( client_utils . signProposal ( signer , discovery_request ) ) ;
990+ const signed_request = client_utils . toEnvelope ( client_utils . signProposal ( signer , discovery_request ) ) ;
989991
990992 const response = await target_peer . sendDiscovery ( signed_request ) ;
991993 logger . debug ( '%s - processing discovery response' , method ) ;
@@ -1479,7 +1481,7 @@ const Channel = class {
14791481 seekPayload . setData ( seekInfo . toBuffer ( ) ) ;
14801482
14811483 // building manually or will get protobuf errors on send
1482- let envelope = client_utils . toEnvelope ( client_utils . signProposal ( signer , seekPayload ) ) ;
1484+ let envelope = client_utils . toEnvelope ( client_utils . signProposal ( signer , seekPayload ) ) ;
14831485 // This will return us a block
14841486 let block = await orderer . sendDeliver ( envelope ) ;
14851487 logger . debug ( '%s - good results from seek block ' , method ) ; // :: %j',results);
@@ -1537,7 +1539,7 @@ const Channel = class {
15371539 seekPayload . setData ( seekInfo . toBuffer ( ) ) ;
15381540
15391541 // building manually or will get protobuf errors on send
1540- envelope = client_utils . toEnvelope ( client_utils . signProposal ( signer , seekPayload ) ) ;
1542+ envelope = client_utils . toEnvelope ( client_utils . signProposal ( signer , seekPayload ) ) ;
15411543 // this will return us a block
15421544 block = await orderer . sendDeliver ( envelope ) ;
15431545 if ( ! block ) {
@@ -2217,8 +2219,8 @@ const Channel = class {
22172219 const lcccSpec = {
22182220 // type: _ccProto.ChaincodeSpec.Type.GOLANG,
22192221 type : client_utils . translateCCType ( request . chaincodeType ) ,
2220- chaincode_id : { name : Constants . LSCC } ,
2221- input : { args : lcccSpec_args }
2222+ chaincode_id : { name : Constants . LSCC } ,
2223+ input : { args : lcccSpec_args }
22222224 } ;
22232225
22242226 const channelHeader = client_utils . buildChannelHeader (
@@ -2247,8 +2249,9 @@ const Channel = class {
22472249 * discovery service if no targets are specified.
22482250 * @property {string } chaincodeId - Required. The id of the chaincode to process
22492251 * the transaction proposal
2250- * @property {TransactionID } txId - Required. TransactionID object with the
2251- * transaction id and nonce
2252+ * @property {TransactionID } txId - Optional. TransactionID object with the
2253+ * transaction id and nonce. txId is required for [sendTransactionProposal]{@link Channel#sendTransactionProposal}
2254+ * and optional for [generateUnsignedProposal]{@link Channel#generateUnsignedProposal}
22522255 * @property {map } transientMap - Optional. <string, byte[]> map that can be
22532256 * used by the chaincode but not
22542257 * saved in the ledger, such as cryptographic information for encryption
@@ -2375,8 +2378,8 @@ const Channel = class {
23752378
23762379 const invokeSpec = {
23772380 type : _ccProto . ChaincodeSpec . Type . GOLANG ,
2378- chaincode_id : { name : request . chaincodeId } ,
2379- input : { args : args }
2381+ chaincode_id : { name : request . chaincodeId } ,
2382+ input : { args : args }
23802383 } ;
23812384
23822385 let signer = null ;
@@ -2400,7 +2403,7 @@ const Channel = class {
24002403 const proposal = client_utils . buildProposal ( invokeSpec , header , request . transientMap ) ;
24012404 const signed_proposal = client_utils . signProposal ( signer , proposal ) ;
24022405
2403- return { signed : signed_proposal , source : proposal } ;
2406+ return { signed : signed_proposal , source : proposal } ;
24042407 }
24052408
24062409 /**
@@ -2452,7 +2455,7 @@ const Channel = class {
24522455 async sendTransaction ( request , timeout ) {
24532456 logger . debug ( 'sendTransaction - start :: channel %s' , this ) ;
24542457
2455- if ( ! request ) {
2458+ if ( ! request ) {
24562459 throw Error ( 'Missing input request object on the transaction request' ) ;
24572460 }
24582461 // Verify that data is being passed in
@@ -2490,7 +2493,7 @@ const Channel = class {
24902493 use_admin_signer = request . txId . isAdmin ( ) ;
24912494 }
24922495
2493- const envelope = Channel . buildEnvelope ( this . _clientContext , chaincodeProposal , endorsements , proposalResponse , use_admin_signer ) ;
2496+ const envelope = Channel . buildEnvelope ( this . _clientContext , chaincodeProposal , endorsements , proposalResponse , use_admin_signer ) ;
24942497
24952498 if ( this . _commit_handler ) {
24962499 const params = {
@@ -2503,15 +2506,225 @@ const Channel = class {
25032506 } else {
25042507 // verify that we have an orderer configured
25052508 const orderer = this . _clientContext . getTargetOrderer ( request . orderer , this . getOrderers ( ) , this . _name ) ;
2506- return orderer . sendBroadcast ( envelope , timeout ) ;
2509+ return orderer . sendBroadcast ( envelope , timeout ) ;
2510+ }
2511+ }
2512+
2513+
2514+ /**
2515+ * @typedef {Object } ProposalRequest
2516+ * @property {string } fcn - Required. The function name.
2517+ * @property {string[] } args - Required. Arguments to send to chaincode.
2518+ * @property {string } chaincodeId - Required. ChaincodeId.
2519+ * @property {Buffer } argbytes - Optional. Include when an argument must be included as bytes.
2520+ * @property {map } transientMap - Optional. <sting, byte[]> The Map that can be
2521+ * used by the chaincode but not saved in the ledger, such as
2522+ * cryptographic information for encryption.
2523+ */
2524+
2525+
2526+ /**
2527+ * Generates the endorse proposal bytes for a transaction
2528+ *
2529+ * Current the [sendTransactionProposal]{@link Channel#sendTransactionProposal}
2530+ * sign a transaction using the user identity from SDK's context (which
2531+ * contains the user's private key).
2532+ *
2533+ * This method is designed to build the proposal bytes at SDK side,
2534+ * and user can sign this proposal with their private key, and send
2535+ * the signed proposal to peer by [sendSignedProposal]
2536+ *
2537+ * so the user's private
2538+ * key would not be required at SDK side.
2539+ *
2540+ * @param {ProposalRequest } request chaincode invoke request
2541+ * @param {string } mspId the mspId for this identity
2542+ * @param {string } certificate PEM encoded certificate
2543+ * @param {boolean } admin if this transaction is invoked by admin
2544+ * @returns {Proposal }
2545+ */
2546+ generateUnsignedProposal ( request , mspId , certificate , admin ) {
2547+ const method = 'generateUnsignedProposal' ;
2548+ logger . debug ( '%s - start' , method ) ;
2549+
2550+ const args = [ ] ;
2551+ args . push ( Buffer . from ( request . fcn ? request . fcn : 'invoke' , 'utf8' ) ) ;
2552+ logger . debug ( '%s - adding function arg:%s' , method , request . fcn ? request . fcn : 'invoke' ) ;
2553+
2554+ // check request && request.chaincodeId
2555+ let errorMsg = client_utils . checkProposalRequest ( request , false ) ;
2556+
2557+ if ( ! request . args ) {
2558+ errorMsg = 'Missing "args" in Transaction proposal request' ;
2559+ }
2560+ if ( ! Array . isArray ( request . args ) ) {
2561+ errorMsg = 'Param "args" in Transaction proposal request should be a string array' ;
2562+ }
2563+ if ( ! request . channelId ) {
2564+ errorMsg = 'Missing Required param "channelId" in Transaction proposal' ;
2565+ }
2566+
2567+ if ( errorMsg ) {
2568+ logger . error ( '%s error %s' , method , errorMsg ) ;
2569+ throw new Error ( errorMsg ) ;
2570+ }
2571+
2572+ request . args . forEach ( arg => {
2573+ logger . debug ( '%s - adding arg %s' , method , arg ) ;
2574+ args . push ( Buffer . from ( arg , 'utf8' ) ) ;
2575+ } ) ;
2576+ //special case to support the bytes argument of the query by hash
2577+ if ( request . argbytes ) {
2578+ logger . debug ( '%s - adding the argument :: argbytes' , method ) ;
2579+ args . push ( request . argbytes ) ;
2580+ } else {
2581+ logger . debug ( '%s - not adding the argument :: argbytes' , method ) ;
2582+ }
2583+
2584+ const invokeSpec = {
2585+ type : _ccProto . ChaincodeSpec . Type . GOLANG ,
2586+ chaincode_id : { name : request . chaincodeId } ,
2587+ input : { args }
2588+ } ;
2589+
2590+ // certificate, publicKey, mspId, cryptoSuite
2591+ const signer = new Identity ( certificate , null , mspId ) ;
2592+ const txId = new TransactionID ( signer , admin ) ;
2593+
2594+ const channelHeader = client_utils . buildChannelHeader (
2595+ _commonProto . HeaderType . ENDORSER_TRANSACTION ,
2596+ request . channelId ,
2597+ txId . getTransactionID ( ) ,
2598+ null ,
2599+ request . chaincodeId ,
2600+ client_utils . buildCurrentTimestamp ( ) ,
2601+ this . _clientContext . getClientCertHash ( )
2602+ ) ;
2603+
2604+ const header = client_utils . buildHeader ( signer , channelHeader , txId . getNonce ( ) ) ;
2605+ const proposal = client_utils . buildProposal ( invokeSpec , header , request . transientMap ) ;
2606+ return { proposal, txId } ;
2607+ }
2608+
2609+ /**
2610+ * @typedef {Object } SignedProposal
2611+ * @property {Peer[] } targets - Required. The function name.
2612+ * @property {Buffer } signedProposal - Required. The signed endorse proposal
2613+ */
2614+
2615+ /**
2616+ * Send signed transaction proposal to peer
2617+ *
2618+ * @param {SignedProposal } request signed endorse transaction proposal, this signed
2619+ * proposal would be send to peer directly.
2620+ * @param {number } timeout the timeout setting passed on sendSignedProposal
2621+ */
2622+ async sendSignedProposal ( request , timeout ) {
2623+ return Channel . sendSignedProposal ( request , timeout ) ;
2624+ }
2625+
2626+ /**
2627+ * Send signed transaction proposal to peer
2628+ *
2629+ * @param {SignedProposal } request signed endorse transaction proposal, this signed
2630+ * proposal would be send to peer directly.
2631+ * @param {number } timeout the timeout setting passed on sendSignedProposal
2632+ */
2633+ static async sendSignedProposal ( request , timeout ) {
2634+ const responses = await client_utils . sendPeersProposal ( request . targets , request . signedProposal , timeout ) ;
2635+ return responses ;
2636+ }
2637+
2638+ /**
2639+ * generate the commit proposal for a transaction
2640+ *
2641+ * @param {TransactionRequest } request
2642+ */
2643+ async generateUnsignedTransaction ( request ) {
2644+ logger . debug ( 'generateUnsignedTransaction - start :: channel %s' , this . _name ) ;
2645+
2646+ if ( ! request ) {
2647+ throw Error ( 'Missing input request object on the generateUnsignedTransaction() call' ) ;
2648+ }
2649+ // Verify that data is being passed in
2650+ if ( ! request . proposalResponses ) {
2651+ throw Error ( 'Missing "proposalResponses" parameter in transaction request' ) ;
2652+ }
2653+ if ( ! request . proposal ) {
2654+ throw Error ( 'Missing "proposal" parameter in transaction request' ) ;
2655+ }
2656+ let proposalResponses = request . proposalResponses ;
2657+ const chaincodeProposal = request . proposal ;
2658+
2659+ const endorsements = [ ] ;
2660+ if ( ! Array . isArray ( proposalResponses ) ) {
2661+ //convert to array
2662+ proposalResponses = [ proposalResponses ] ;
2663+ }
2664+ for ( const proposalResponse of proposalResponses ) {
2665+ // make sure only take the valid responses to set on the consolidated response object
2666+ // to use in the transaction object
2667+ if ( proposalResponse && proposalResponse . response && proposalResponse . response . status === 200 ) {
2668+ endorsements . push ( proposalResponse . endorsement ) ;
2669+ }
2670+ }
2671+
2672+ if ( endorsements . length < 1 ) {
2673+ logger . error ( 'sendTransaction - no valid endorsements found' ) ;
2674+ throw new Error ( 'no valid endorsements found' ) ;
2675+ }
2676+ const proposalResponse = proposalResponses [ 0 ] ;
2677+
2678+ let use_admin_signer = false ;
2679+ if ( request . txId ) {
2680+ use_admin_signer = request . txId . isAdmin ( ) ;
2681+ }
2682+
2683+ const proposal = ChannelHelper . buildTransactionProposal (
2684+ chaincodeProposal ,
2685+ endorsements ,
2686+ proposalResponse ,
2687+ use_admin_signer
2688+ ) ;
2689+ return proposal ;
2690+ }
2691+
2692+ /**
2693+ * @typedef {Object } SignedCommitProposal
2694+ * @property {TransactionRequest } request - Required. The commit request
2695+ * @property {Buffer } signedTransaction - Required. The signed transaction
2696+ * @property {Orderer|string } orderer - Optional. The orderer instance or string name
2697+ * of the orderer to operate. See {@link Client.getTargetOrderer}
2698+ */
2699+
2700+ /**
2701+ * send the signed commit proposal for a transaction
2702+ *
2703+ * @param {SignedCommitProposal } request the signed commit proposal
2704+ * @param {number } timeout the timeout setting passed on sendSignedProposal
2705+ */
2706+ async sendSignedTransaction ( request , timeout ) {
2707+ const signed_envelope = client_utils . toEnvelope ( request . signedProposal ) ;
2708+ if ( this . _commit_handler ) {
2709+ const params = {
2710+ signed_envelope,
2711+ request : request . request ,
2712+ timeout : timeout
2713+ } ;
2714+
2715+ return this . _commit_handler . commit ( params ) ;
2716+ } else {
2717+ // verify that we have an orderer configured
2718+ const orderer = this . _clientContext . getTargetOrderer ( request . orderer , this . getOrderers ( ) , this . _name ) ;
2719+ return orderer . sendBroadcast ( signed_envelope , timeout ) ;
25072720 }
25082721 }
25092722
25102723 /*
25112724 * Internal static method to allow transaction envelop to be built without
25122725 * creating a new channel
25132726 */
2514- static buildEnvelope ( clientContext , chaincodeProposal , endorsements , proposalResponse , use_admin_signer ) {
2727+ static buildEnvelope ( clientContext , chaincodeProposal , endorsements , proposalResponse , use_admin_signer ) {
25152728
25162729 const header = _commonProto . Header . decode ( chaincodeProposal . getHeader ( ) ) ;
25172730
@@ -2548,7 +2761,7 @@ const Channel = class {
25482761 payload . setData ( transaction . toBuffer ( ) ) ;
25492762
25502763 const signer = clientContext . _getSigningIdentity ( use_admin_signer ) ;
2551- return client_utils . toEnvelope ( client_utils . signProposal ( signer , payload ) ) ;
2764+ return client_utils . toEnvelope ( client_utils . signProposal ( signer , payload ) ) ;
25522765 }
25532766 /**
25542767 * @typedef {Object } ChaincodeQueryRequest
0 commit comments