@@ -21,6 +21,7 @@ package fabricclient
2121
2222import (
2323 "fmt"
24+ "strconv"
2425 "sync"
2526
2627 "time"
@@ -57,21 +58,24 @@ type Chain interface {
5758 AddPeer (peer Peer )
5859 RemovePeer (peer Peer )
5960 GetPeers () []Peer
61+ SetPrimaryPeer (peer Peer ) error
62+ GetPrimaryPeer () Peer
6063 AddOrderer (orderer Orderer )
6164 RemoveOrderer (orderer Orderer )
6265 GetOrderers () []Orderer
6366 InitializeChain () bool
6467 UpdateChain () bool
6568 IsReadonly () bool
66- QueryInfo ()
67- QueryBlock (blockNumber int )
68- QueryTransaction (transactionID int )
69+ QueryInfo () (* common.BlockchainInfo , error )
70+ QueryBlock (blockNumber int ) (* common.Block , error )
71+ QueryBlockByHash (blockHash []byte ) (* common.Block , error )
72+ QueryTransaction (transactionID string ) (* pb.ProcessedTransaction , error )
6973 CreateTransactionProposal (chaincodeName string , chainID string , args []string , sign bool , transientData map [string ][]byte ) (* TransactionProposal , error )
70- SendTransactionProposal (proposal * TransactionProposal , retry int ) ([]* TransactionProposalResponse , error )
74+ SendTransactionProposal (proposal * TransactionProposal , retry int , targets [] Peer ) ([]* TransactionProposalResponse , error )
7175 CreateTransaction (resps []* TransactionProposalResponse ) (* Transaction , error )
7276 SendTransaction (tx * Transaction ) ([]* TransactionResponse , error )
73- SendInstallProposal (chaincodeName string , chaincodePath string , chaincodeVersion string , chaincodePackage []byte ) ([]* TransactionProposalResponse , string , error )
74- SendInstantiateProposal (chaincodeName string , chainID string , args []string , chaincodePath string , chaincodeVersion string ) ([]* TransactionProposalResponse , string , error )
77+ SendInstallProposal (chaincodeName string , chaincodePath string , chaincodeVersion string , chaincodePackage []byte , targets [] Peer ) ([]* TransactionProposalResponse , string , error )
78+ SendInstantiateProposal (chaincodeName string , chainID string , args []string , chaincodePath string , chaincodeVersion string , targets [] Peer ) ([]* TransactionProposalResponse , string , error )
7579}
7680
7781type chain struct {
@@ -81,6 +85,7 @@ type chain struct {
8185 tcertBatchSize int // The number of tcerts to get in each batch
8286 orderers map [string ]Orderer
8387 clientContext Client
88+ primaryPeer Peer
8489}
8590
8691// The TransactionProposal object to be send to the endorsers
@@ -221,6 +226,80 @@ func (c *chain) GetPeers() []Peer {
221226 return peersArray
222227}
223228
229+ /**
230+ * Utility function to get target peers (target peer is valid only if it belongs to chain's peer list).
231+ * If targets is empty return chain's peer list
232+ * @returns {[]Peer} The target peer list
233+ * @returns {error} if target peer is not in chain's peer list
234+ */
235+ func (c * chain ) getTargetPeers (targets []Peer ) ([]Peer , error ) {
236+
237+ if targets == nil || len (targets ) == 0 {
238+ return c .GetPeers (), nil
239+ }
240+
241+ var targetPeers []Peer
242+ for _ , target := range targets {
243+ if ! c .isValidPeer (target ) {
244+ return nil , fmt .Errorf ("The target peer must be on this chain peer list" )
245+ }
246+ targetPeers = append (targetPeers , c .peers [target .GetURL ()])
247+ }
248+
249+ return targetPeers , nil
250+ }
251+
252+ /**
253+ * Utility function to ensure that a peer exists on this chain
254+ * @returns {bool} true if peer exists on this chain
255+ */
256+ func (c * chain ) isValidPeer (peer Peer ) bool {
257+ return peer != nil && c .peers [peer .GetURL ()] != nil
258+ }
259+
260+ // SetPrimaryPeer ...
261+ /**
262+ * Set the primary peer
263+ * The peer to use for doing queries.
264+ * Peer must be a peer on this chain's peer list.
265+ * Default: When no primary peer has been set the first peer
266+ * on the list will be used.
267+ * @param {Peer} peer An instance of the Peer class.
268+ * @returns error when peer is not on the existing peer list
269+ */
270+ func (c * chain ) SetPrimaryPeer (peer Peer ) error {
271+
272+ if ! c .isValidPeer (peer ) {
273+ return fmt .Errorf ("The primary peer must be on this chain peer list" )
274+ }
275+
276+ c .primaryPeer = c .peers [peer .GetURL ()]
277+ return nil
278+ }
279+
280+ // GetPrimaryPeer ...
281+ /**
282+ * Get the primary peer
283+ * The peer to use for doing queries.
284+ * Default: When no primary peer has been set the first peer
285+ * from map range will be used.
286+ * @returns {Peer} peer An instance of the Peer class.
287+ */
288+ func (c * chain ) GetPrimaryPeer () Peer {
289+
290+ if c .primaryPeer != nil {
291+ return c .primaryPeer
292+ }
293+
294+ // When no primary peer has been set default to the first peer
295+ // from map range - order is not guaranteed
296+ for _ , peer := range c .peers {
297+ return peer
298+ }
299+
300+ return nil
301+ }
302+
224303// AddOrderer ...
225304/**
226305 * Add orderer endpoint to a chain object, this is a local-only operation.
@@ -297,8 +376,25 @@ func (c *chain) IsReadonly() bool {
297376 * (height, known peers).
298377 * @returns {object} With height, currently the only useful info.
299378 */
300- func (c * chain ) QueryInfo () {
301- //to do
379+ func (c * chain ) QueryInfo () (* common.BlockchainInfo , error ) {
380+
381+ // prepare arguments to call qscc GetChainInfo function
382+ var args []string
383+ args = append (args , "GetChainInfo" )
384+ args = append (args , c .GetName ())
385+
386+ payload , err := c .query (args )
387+ if err != nil {
388+ return nil , fmt .Errorf ("Invoke qscc GetChainInfo return error: %v" , err )
389+ }
390+
391+ bci := & common.BlockchainInfo {}
392+ err = proto .Unmarshal (payload , bci )
393+ if err != nil {
394+ return nil , fmt .Errorf ("Unmarshal BlockchainInfo return error: %v" , err )
395+ }
396+
397+ return bci , nil
302398}
303399
304400// QueryBlock ...
@@ -307,18 +403,128 @@ func (c *chain) QueryInfo() {
307403 * @param {int} blockNumber The number which is the ID of the Block.
308404 * @returns {object} Object containing the block.
309405 */
310- func (c * chain ) QueryBlock (blockNumber int ) {
311- //to do
406+ func (c * chain ) QueryBlock (blockNumber int ) (* common.Block , error ) {
407+
408+ if blockNumber < 0 {
409+ return nil , fmt .Errorf ("Block number must be positive integer" )
410+ }
411+
412+ // prepare arguments to call qscc GetBlockByNumber function
413+ var args []string
414+ args = append (args , "GetBlockByNumber" )
415+ args = append (args , c .GetName ())
416+ args = append (args , strconv .Itoa (blockNumber ))
417+
418+ payload , err := c .query (args )
419+ if err != nil {
420+ return nil , fmt .Errorf ("Invoke qscc GetBlockByNumber return error: %v" , err )
421+ }
422+
423+ block := & common.Block {}
424+ err = proto .Unmarshal (payload , block )
425+ if err != nil {
426+ return nil , fmt .Errorf ("Unmarshal Block return error: %v" , err )
427+ }
428+
429+ return block , nil
430+ }
431+
432+ // QueryBlockByHash ...
433+ /**
434+ * Queries the ledger for Block by block hash.
435+ * This query will be made to the primary peer.
436+ * @param {byte[]} block hash of the Block.
437+ * @returns {object} Object containing the block.
438+ */
439+ func (c * chain ) QueryBlockByHash (blockHash []byte ) (* common.Block , error ) {
440+
441+ if blockHash == nil {
442+ return nil , fmt .Errorf ("Blockhash bytes are required" )
443+ }
444+
445+ // prepare arguments to call qscc GetBlockByNumber function
446+ var args []string
447+ args = append (args , "GetBlockByHash" )
448+ args = append (args , c .GetName ())
449+ args = append (args , string (blockHash [:len (blockHash )]))
450+
451+ payload , err := c .query (args )
452+ if err != nil {
453+ return nil , fmt .Errorf ("Invoke qscc GetBlockByHash return error: %v" , err )
454+ }
455+
456+ block := & common.Block {}
457+ err = proto .Unmarshal (payload , block )
458+ if err != nil {
459+ return nil , fmt .Errorf ("Unmarshal Block return error: %v" , err )
460+ }
461+
462+ return block , nil
312463}
313464
314465// QueryTransaction ...
315466/**
316- * Queries the ledger for Transaction by number.
317- * @param {int} transactionID
318- * @returns {object} Transaction information containing the transaction.
467+ * Queries the ledger for Transaction by number.
468+ * This query will be made to the primary peer.
469+ * @param {int} transactionID
470+ * @returns {object} ProcessedTransaction information containing the transaction.
319471 */
320- func (c * chain ) QueryTransaction (transactionID int ) {
321- //to do
472+ func (c * chain ) QueryTransaction (transactionID string ) (* pb.ProcessedTransaction , error ) {
473+
474+ // prepare arguments to call qscc GetTransactionByID function
475+ var args []string
476+ args = append (args , "GetTransactionByID" )
477+ args = append (args , c .GetName ())
478+ args = append (args , transactionID )
479+
480+ payload , err := c .query (args )
481+ if err != nil {
482+ return nil , fmt .Errorf ("Invoke qscc GetBlockByNumber return error: %v" , err )
483+ }
484+
485+ transaction := new (pb.ProcessedTransaction )
486+ err = proto .Unmarshal (payload , transaction )
487+ if err != nil {
488+ return nil , fmt .Errorf ("Unmarshal ProcessedTransaction return error: %v" , err )
489+ }
490+
491+ return transaction , nil
492+ }
493+
494+ /**
495+ * Generic query functionality for qscc
496+ * This query will be made to the primary peer.
497+ * @param {[]string} invoke arguments
498+ * @returns {[]byte} payload
499+ */
500+ func (c * chain ) query (args []string ) ([]byte , error ) {
501+
502+ signedProposal , err := c .CreateTransactionProposal ("qscc" , "" , args , true , nil )
503+ if err != nil {
504+ return nil , fmt .Errorf ("query - CreateTransactionProposal return error: %v" , err )
505+ }
506+
507+ primary := c .GetPrimaryPeer ()
508+
509+ logger .Debugf ("Calling QSCC function %v on primary: %s\n " , args [0 ], primary .GetURL ())
510+
511+ transactionProposalResponses , err := c .SendTransactionProposal (signedProposal , 0 , []Peer {primary })
512+ if err != nil {
513+ return nil , fmt .Errorf ("query - SendTransactionProposal return error: %v" , err )
514+ }
515+
516+ // we are only querying the primary peer hence one result
517+ if len (transactionProposalResponses ) != 1 {
518+ return nil , fmt .Errorf ("query - Should have one result only - result number: %d" , len (transactionProposalResponses ))
519+ }
520+
521+ response := transactionProposalResponses [0 ]
522+ if response .Err != nil {
523+ return nil , fmt .Errorf ("query qscc %s return error: %v" , response .Endorser , response .Err )
524+ }
525+
526+ return response .GetResponsePayload (), nil
527+
322528}
323529
324530// CreateTransactionProposal ...
@@ -373,7 +579,7 @@ func (c *chain) CreateTransactionProposal(chaincodeName string, chainID string,
373579
374580// SendTransactionProposal ...
375581// Send the created proposal to peer for endorsement.
376- func (c * chain ) SendTransactionProposal (proposal * TransactionProposal , retry int ) ([]* TransactionProposalResponse , error ) {
582+ func (c * chain ) SendTransactionProposal (proposal * TransactionProposal , retry int , targets [] Peer ) ([]* TransactionProposalResponse , error ) {
377583 if c .peers == nil || len (c .peers ) == 0 {
378584 return nil , fmt .Errorf ("peers is nil" )
379585 }
@@ -385,7 +591,12 @@ func (c *chain) SendTransactionProposal(proposal *TransactionProposal, retry int
385591 var transactionProposalResponses []* TransactionProposalResponse
386592 var wg sync.WaitGroup
387593
388- for _ , p := range c .peers {
594+ targetPeers , err := c .getTargetPeers (targets )
595+ if err != nil {
596+ return nil , fmt .Errorf ("GetTargetPeers return error: %s" , err )
597+ }
598+
599+ for _ , p := range targetPeers {
389600 wg .Add (1 )
390601 go func (peer Peer ) {
391602 defer wg .Done ()
@@ -564,7 +775,7 @@ func (c *chain) SendTransaction(tx *Transaction) ([]*TransactionResponse, error)
564775* @param {[]string} chaincodeVersion: required - string of the version of the chaincode
565776* @param {[]string} chaincodeVersion: optional - Array of byte the chaincodePackage
566777 */
567- func (c * chain ) SendInstallProposal (chaincodeName string , chaincodePath string , chaincodeVersion string , chaincodePackage []byte ) ([]* TransactionProposalResponse , string , error ) {
778+ func (c * chain ) SendInstallProposal (chaincodeName string , chaincodePath string , chaincodeVersion string , chaincodePackage []byte , targets [] Peer ) ([]* TransactionProposalResponse , string , error ) {
568779
569780 if chaincodeName == "" {
570781 return nil , "" , fmt .Errorf ("Missing 'chaincodeName' parameter" )
@@ -584,6 +795,11 @@ func (c *chain) SendInstallProposal(chaincodeName string, chaincodePath string,
584795 }
585796 }
586797
798+ targetPeers , err := c .getTargetPeers (targets )
799+ if err != nil {
800+ return nil , "" , fmt .Errorf ("Invalid target peers return error: %s" , err )
801+ }
802+
587803 now := time .Now ()
588804 cds := & pb.ChaincodeDeploymentSpec {ChaincodeSpec : & pb.ChaincodeSpec {
589805 Type : pb .ChaincodeSpec_GOLANG , ChaincodeId : & pb.ChaincodeID {Name : chaincodeName , Path : chaincodePath , Version : chaincodeVersion }},
@@ -614,7 +830,7 @@ func (c *chain) SendInstallProposal(chaincodeName string, chaincodePath string,
614830 signedProposal : signedProposal ,
615831 proposal : proposal ,
616832 TransactionID : txID ,
617- }, 0 )
833+ }, 0 , targetPeers )
618834 return transactionProposalResponse , txID , err
619835}
620836
@@ -628,7 +844,7 @@ func (c *chain) SendInstallProposal(chaincodeName string, chaincodePath string,
628844* @param {[]string} chaincodeVersion: required - string of the version of the chaincode
629845 */
630846func (c * chain ) SendInstantiateProposal (chaincodeName string , chainID string ,
631- args []string , chaincodePath string , chaincodeVersion string ) ([]* TransactionProposalResponse , string , error ) {
847+ args []string , chaincodePath string , chaincodeVersion string , targets [] Peer ) ([]* TransactionProposalResponse , string , error ) {
632848
633849 if chaincodeName == "" {
634850 return nil , "" , fmt .Errorf ("Missing 'chaincodeName' parameter" )
@@ -643,6 +859,11 @@ func (c *chain) SendInstantiateProposal(chaincodeName string, chainID string,
643859 return nil , "" , fmt .Errorf ("Missing 'chaincodeVersion' parameter" )
644860 }
645861
862+ targetPeers , err := c .getTargetPeers (targets )
863+ if err != nil {
864+ return nil , "" , fmt .Errorf ("GetTargetPeers return error: %s" , err )
865+ }
866+
646867 argsArray := make ([][]byte , len (args ))
647868 for i , arg := range args {
648869 argsArray [i ] = []byte (arg )
@@ -684,7 +905,7 @@ func (c *chain) SendInstantiateProposal(chaincodeName string, chainID string,
684905 signedProposal : signedProposal ,
685906 proposal : proposal ,
686907 TransactionID : txID ,
687- }, 0 )
908+ }, 0 , targetPeers )
688909
689910 return transactionProposalResponse , txID , err
690911}
0 commit comments