Skip to content
This repository was archived by the owner on Apr 25, 2025. It is now read-only.

Commit ca69dcd

Browse files
committed
[FAB-2898] SDK Go - Query SCC Support
Change-Id: Idc4e862b4904cb90cce666348d340e97ba87f246 Signed-off-by: Sandra Vrtikapa <sandra.vrtikapa@securekey.com>
1 parent 405b815 commit ca69dcd

File tree

6 files changed

+489
-40
lines changed

6 files changed

+489
-40
lines changed

fabric-client/chain.go

Lines changed: 242 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ package fabricclient
2121

2222
import (
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

7781
type 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
*/
630846
func (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

Comments
 (0)