github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/scc/qscc/query.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package qscc
     8  
     9  import (
    10  	"fmt"
    11  	"strconv"
    12  
    13  	"github.com/hechain20/hechain/common/flogging"
    14  	"github.com/hechain20/hechain/core/aclmgmt"
    15  	"github.com/hechain20/hechain/core/ledger"
    16  	"github.com/hechain20/hechain/protoutil"
    17  	"github.com/hyperledger/fabric-chaincode-go/shim"
    18  	pb "github.com/hyperledger/fabric-protos-go/peer"
    19  )
    20  
    21  // LedgerGetter gets the PeerLedger associated with a channel.
    22  type LedgerGetter interface {
    23  	GetLedger(cid string) ledger.PeerLedger
    24  }
    25  
    26  // New returns an instance of QSCC.
    27  // Typically this is called once per peer.
    28  func New(aclProvider aclmgmt.ACLProvider, ledgers LedgerGetter) *LedgerQuerier {
    29  	return &LedgerQuerier{
    30  		aclProvider: aclProvider,
    31  		ledgers:     ledgers,
    32  	}
    33  }
    34  
    35  func (e *LedgerQuerier) Name() string              { return "qscc" }
    36  func (e *LedgerQuerier) Chaincode() shim.Chaincode { return e }
    37  
    38  // LedgerQuerier implements the ledger query functions, including:
    39  // - GetChainInfo returns BlockchainInfo
    40  // - GetBlockByNumber returns a block
    41  // - GetBlockByHash returns a block
    42  // - GetTransactionByID returns a transaction
    43  type LedgerQuerier struct {
    44  	aclProvider aclmgmt.ACLProvider
    45  	ledgers     LedgerGetter
    46  }
    47  
    48  var qscclogger = flogging.MustGetLogger("qscc")
    49  
    50  // These are function names from Invoke first parameter
    51  const (
    52  	GetChainInfo       string = "GetChainInfo"
    53  	GetBlockByNumber   string = "GetBlockByNumber"
    54  	GetBlockByHash     string = "GetBlockByHash"
    55  	GetTransactionByID string = "GetTransactionByID"
    56  	GetBlockByTxID     string = "GetBlockByTxID"
    57  )
    58  
    59  // Init is called once per chain when the chain is created.
    60  // This allows the chaincode to initialize any variables on the ledger prior
    61  // to any transaction execution on the chain.
    62  func (e *LedgerQuerier) Init(stub shim.ChaincodeStubInterface) pb.Response {
    63  	qscclogger.Info("Init QSCC")
    64  
    65  	return shim.Success(nil)
    66  }
    67  
    68  // Invoke is called with args[0] contains the query function name, args[1]
    69  // contains the chain ID, which is temporary for now until it is part of stub.
    70  // Each function requires additional parameters as described below:
    71  // # GetChainInfo: Return a BlockchainInfo object marshalled in bytes
    72  // # GetBlockByNumber: Return the block specified by block number in args[2]
    73  // # GetBlockByHash: Return the block specified by block hash in args[2]
    74  // # GetTransactionByID: Return the transaction specified by ID in args[2]
    75  func (e *LedgerQuerier) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
    76  	args := stub.GetArgs()
    77  
    78  	if len(args) < 2 {
    79  		return shim.Error(fmt.Sprintf("Incorrect number of arguments, %d", len(args)))
    80  	}
    81  
    82  	fname := string(args[0])
    83  	cid := string(args[1])
    84  
    85  	sp, err := stub.GetSignedProposal()
    86  	if err != nil {
    87  		return shim.Error(fmt.Sprintf("Failed getting signed proposal from stub, %s: %s", cid, err))
    88  	}
    89  
    90  	name, err := protoutil.InvokedChaincodeName(sp.ProposalBytes)
    91  	if err != nil {
    92  		return shim.Error(fmt.Sprintf("Failed to identify the called chaincode: %s", err))
    93  	}
    94  
    95  	if name != e.Name() {
    96  		return shim.Error(fmt.Sprintf("Rejecting invoke of QSCC from another chaincode because of potential for deadlocks, original invocation for '%s'", name))
    97  	}
    98  
    99  	if fname != GetChainInfo && len(args) < 3 {
   100  		return shim.Error(fmt.Sprintf("missing 3rd argument for %s", fname))
   101  	}
   102  
   103  	targetLedger := e.ledgers.GetLedger(cid)
   104  	if targetLedger == nil {
   105  		return shim.Error(fmt.Sprintf("Invalid chain ID, %s", cid))
   106  	}
   107  
   108  	qscclogger.Debugf("Invoke function: %s on chain: %s", fname, cid)
   109  
   110  	// Handle ACL:
   111  	res := getACLResource(fname)
   112  	if err = e.aclProvider.CheckACL(res, cid, sp); err != nil {
   113  		return shim.Error(fmt.Sprintf("access denied for [%s][%s]: [%s]", fname, cid, err))
   114  	}
   115  
   116  	switch fname {
   117  	case GetTransactionByID:
   118  		return getTransactionByID(targetLedger, args[2])
   119  	case GetBlockByNumber:
   120  		return getBlockByNumber(targetLedger, args[2])
   121  	case GetBlockByHash:
   122  		return getBlockByHash(targetLedger, args[2])
   123  	case GetChainInfo:
   124  		return getChainInfo(targetLedger)
   125  	case GetBlockByTxID:
   126  		return getBlockByTxID(targetLedger, args[2])
   127  	}
   128  
   129  	return shim.Error(fmt.Sprintf("Requested function %s not found.", fname))
   130  }
   131  
   132  func getTransactionByID(vledger ledger.PeerLedger, tid []byte) pb.Response {
   133  	if tid == nil {
   134  		return shim.Error("Transaction ID must not be nil.")
   135  	}
   136  
   137  	processedTran, err := vledger.GetTransactionByID(string(tid))
   138  	if err != nil {
   139  		return shim.Error(fmt.Sprintf("Failed to get transaction with id %s, error %s", string(tid), err))
   140  	}
   141  
   142  	bytes, err := protoutil.Marshal(processedTran)
   143  	if err != nil {
   144  		return shim.Error(err.Error())
   145  	}
   146  
   147  	return shim.Success(bytes)
   148  }
   149  
   150  func getBlockByNumber(vledger ledger.PeerLedger, number []byte) pb.Response {
   151  	if number == nil {
   152  		return shim.Error("Block number must not be nil.")
   153  	}
   154  	bnum, err := strconv.ParseUint(string(number), 10, 64)
   155  	if err != nil {
   156  		return shim.Error(fmt.Sprintf("Failed to parse block number with error %s", err))
   157  	}
   158  	block, err := vledger.GetBlockByNumber(bnum)
   159  	if err != nil {
   160  		return shim.Error(fmt.Sprintf("Failed to get block number %d, error %s", bnum, err))
   161  	}
   162  	// TODO: consider trim block content before returning
   163  	//  Specifically, trim transaction 'data' out of the transaction array Payloads
   164  	//  This will preserve the transaction Payload header,
   165  	//  and client can do GetTransactionByID() if they want the full transaction details
   166  
   167  	bytes, err := protoutil.Marshal(block)
   168  	if err != nil {
   169  		return shim.Error(err.Error())
   170  	}
   171  
   172  	return shim.Success(bytes)
   173  }
   174  
   175  func getBlockByHash(vledger ledger.PeerLedger, hash []byte) pb.Response {
   176  	if hash == nil {
   177  		return shim.Error("Block hash must not be nil.")
   178  	}
   179  	block, err := vledger.GetBlockByHash(hash)
   180  	if err != nil {
   181  		return shim.Error(fmt.Sprintf("Failed to get block hash %s, error %s", string(hash), err))
   182  	}
   183  	// TODO: consider trim block content before returning
   184  	//  Specifically, trim transaction 'data' out of the transaction array Payloads
   185  	//  This will preserve the transaction Payload header,
   186  	//  and client can do GetTransactionByID() if they want the full transaction details
   187  
   188  	bytes, err := protoutil.Marshal(block)
   189  	if err != nil {
   190  		return shim.Error(err.Error())
   191  	}
   192  
   193  	return shim.Success(bytes)
   194  }
   195  
   196  func getChainInfo(vledger ledger.PeerLedger) pb.Response {
   197  	binfo, err := vledger.GetBlockchainInfo()
   198  	if err != nil {
   199  		return shim.Error(fmt.Sprintf("Failed to get block info with error %s", err))
   200  	}
   201  	bytes, err := protoutil.Marshal(binfo)
   202  	if err != nil {
   203  		return shim.Error(err.Error())
   204  	}
   205  
   206  	return shim.Success(bytes)
   207  }
   208  
   209  func getBlockByTxID(vledger ledger.PeerLedger, rawTxID []byte) pb.Response {
   210  	txID := string(rawTxID)
   211  	block, err := vledger.GetBlockByTxID(txID)
   212  	if err != nil {
   213  		return shim.Error(fmt.Sprintf("Failed to get block for txID %s, error %s", txID, err))
   214  	}
   215  
   216  	bytes, err := protoutil.Marshal(block)
   217  	if err != nil {
   218  		return shim.Error(err.Error())
   219  	}
   220  
   221  	return shim.Success(bytes)
   222  }
   223  
   224  func getACLResource(fname string) string {
   225  	return "qscc/" + fname
   226  }