@@ -22,6 +22,8 @@ const MSPManager = require('./msp/msp-manager.js');
22
22
const Policy = require ( './Policy.js' ) ;
23
23
const Constants = require ( './Constants.js' ) ;
24
24
const CollectionConfig = require ( './SideDB.js' ) ;
25
+ const { Identity } = require ( './msp/identity.js' ) ;
26
+ const ChannelHelper = require ( './utils/ChannelHelper' ) ;
25
27
26
28
const _ccProto = grpc . load ( __dirname + '/protos/peer/chaincode.proto' ) . protos ;
27
29
const _transProto = grpc . load ( __dirname + '/protos/peer/transaction.proto' ) . protos ;
@@ -43,7 +45,7 @@ const _discoveryProto = grpc.load(__dirname + '/protos/discovery/protocol.proto'
43
45
const _gossipProto = grpc . load ( __dirname + '/protos/gossip/message.proto' ) . gossip ;
44
46
const _collectionProto = grpc . load ( __dirname + '/protos/common/collection.proto' ) . common ;
45
47
46
- const ImplicitMetaPolicy_Rule = { 0 : 'ANY' , 1 : 'ALL' , 2 : 'MAJORITY' } ;
48
+ const ImplicitMetaPolicy_Rule = { 0 : 'ANY' , 1 : 'ALL' , 2 : 'MAJORITY' } ;
47
49
48
50
const PEER_NOT_ASSIGNED_MSG = 'Peer with name "%s" not assigned to this channel' ;
49
51
const ORDERER_NOT_ASSIGNED_MSG = 'Orderer with name "%s" not assigned to this channel' ;
@@ -92,7 +94,7 @@ const Channel = class {
92
94
}
93
95
const channelNameRegxChecker = sdk_utils . getConfigSetting ( 'channel-name-regx-checker' ) ;
94
96
if ( channelNameRegxChecker ) {
95
- const { pattern, flags} = channelNameRegxChecker ;
97
+ const { pattern, flags } = channelNameRegxChecker ;
96
98
const namePattern = new RegExp ( pattern ? pattern : '' , flags ? flags : '' ) ;
97
99
if ( ! ( name . match ( namePattern ) ) ) {
98
100
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 {
485
487
const keys = Object . keys ( msps ) ;
486
488
for ( const key in keys ) {
487
489
const msp = msps [ keys [ key ] ] ;
488
- const msp_org = { id : msp . getId ( ) } ;
490
+ const msp_org = { id : msp . getId ( ) } ;
489
491
logger . debug ( '%s - found %j' , method , msp_org ) ;
490
492
orgs . push ( msp_org ) ;
491
493
}
@@ -871,7 +873,7 @@ const Channel = class {
871
873
seekPayload . setHeader ( seekHeader ) ;
872
874
seekPayload . setData ( seekInfo . toBuffer ( ) ) ;
873
875
// 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 ) ) ;
875
877
876
878
return orderer . sendDeliver ( envelope ) ;
877
879
}
@@ -985,7 +987,7 @@ const Channel = class {
985
987
discovery_request . setQueries ( queries ) ;
986
988
987
989
// 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 ) ) ;
989
991
990
992
const response = await target_peer . sendDiscovery ( signed_request ) ;
991
993
logger . debug ( '%s - processing discovery response' , method ) ;
@@ -1479,7 +1481,7 @@ const Channel = class {
1479
1481
seekPayload . setData ( seekInfo . toBuffer ( ) ) ;
1480
1482
1481
1483
// 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 ) ) ;
1483
1485
// This will return us a block
1484
1486
let block = await orderer . sendDeliver ( envelope ) ;
1485
1487
logger . debug ( '%s - good results from seek block ' , method ) ; // :: %j',results);
@@ -1537,7 +1539,7 @@ const Channel = class {
1537
1539
seekPayload . setData ( seekInfo . toBuffer ( ) ) ;
1538
1540
1539
1541
// 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 ) ) ;
1541
1543
// this will return us a block
1542
1544
block = await orderer . sendDeliver ( envelope ) ;
1543
1545
if ( ! block ) {
@@ -2217,8 +2219,8 @@ const Channel = class {
2217
2219
const lcccSpec = {
2218
2220
// type: _ccProto.ChaincodeSpec.Type.GOLANG,
2219
2221
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 }
2222
2224
} ;
2223
2225
2224
2226
const channelHeader = client_utils . buildChannelHeader (
@@ -2247,8 +2249,9 @@ const Channel = class {
2247
2249
* discovery service if no targets are specified.
2248
2250
* @property {string } chaincodeId - Required. The id of the chaincode to process
2249
2251
* 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}
2252
2255
* @property {map } transientMap - Optional. <string, byte[]> map that can be
2253
2256
* used by the chaincode but not
2254
2257
* saved in the ledger, such as cryptographic information for encryption
@@ -2375,8 +2378,8 @@ const Channel = class {
2375
2378
2376
2379
const invokeSpec = {
2377
2380
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 }
2380
2383
} ;
2381
2384
2382
2385
let signer = null ;
@@ -2400,7 +2403,7 @@ const Channel = class {
2400
2403
const proposal = client_utils . buildProposal ( invokeSpec , header , request . transientMap ) ;
2401
2404
const signed_proposal = client_utils . signProposal ( signer , proposal ) ;
2402
2405
2403
- return { signed : signed_proposal , source : proposal } ;
2406
+ return { signed : signed_proposal , source : proposal } ;
2404
2407
}
2405
2408
2406
2409
/**
@@ -2452,7 +2455,7 @@ const Channel = class {
2452
2455
async sendTransaction ( request , timeout ) {
2453
2456
logger . debug ( 'sendTransaction - start :: channel %s' , this ) ;
2454
2457
2455
- if ( ! request ) {
2458
+ if ( ! request ) {
2456
2459
throw Error ( 'Missing input request object on the transaction request' ) ;
2457
2460
}
2458
2461
// Verify that data is being passed in
@@ -2490,7 +2493,7 @@ const Channel = class {
2490
2493
use_admin_signer = request . txId . isAdmin ( ) ;
2491
2494
}
2492
2495
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 ) ;
2494
2497
2495
2498
if ( this . _commit_handler ) {
2496
2499
const params = {
@@ -2503,15 +2506,225 @@ const Channel = class {
2503
2506
} else {
2504
2507
// verify that we have an orderer configured
2505
2508
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 ) ;
2507
2720
}
2508
2721
}
2509
2722
2510
2723
/*
2511
2724
* Internal static method to allow transaction envelop to be built without
2512
2725
* creating a new channel
2513
2726
*/
2514
- static buildEnvelope ( clientContext , chaincodeProposal , endorsements , proposalResponse , use_admin_signer ) {
2727
+ static buildEnvelope ( clientContext , chaincodeProposal , endorsements , proposalResponse , use_admin_signer ) {
2515
2728
2516
2729
const header = _commonProto . Header . decode ( chaincodeProposal . getHeader ( ) ) ;
2517
2730
@@ -2548,7 +2761,7 @@ const Channel = class {
2548
2761
payload . setData ( transaction . toBuffer ( ) ) ;
2549
2762
2550
2763
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 ) ) ;
2552
2765
}
2553
2766
/**
2554
2767
* @typedef {Object } ChaincodeQueryRequest
0 commit comments