github.com/inklabsfoundation/inkchain@v0.17.1-0.20181025012015-c3cef8062f19/core/scc/qscc/query.go (about)

     1  /*
     2  Copyright IBM Corp. 2017 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  		 http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package qscc
    18  
    19  import (
    20  	"fmt"
    21  	"strconv"
    22  
    23  	"github.com/inklabsfoundation/inkchain/common/flogging"
    24  
    25  	"github.com/inklabsfoundation/inkchain/common/policies"
    26  	"github.com/inklabsfoundation/inkchain/core/chaincode/shim"
    27  	"github.com/inklabsfoundation/inkchain/core/ledger"
    28  	"github.com/inklabsfoundation/inkchain/core/peer"
    29  	"github.com/inklabsfoundation/inkchain/core/policy"
    30  	"github.com/inklabsfoundation/inkchain/msp/mgmt"
    31  	pb "github.com/inklabsfoundation/inkchain/protos/peer"
    32  	"github.com/inklabsfoundation/inkchain/protos/utils"
    33  )
    34  
    35  // LedgerQuerier implements the ledger query functions, including:
    36  // - GetChainInfo returns BlockchainInfo
    37  // - GetBlockByNumber returns a block
    38  // - GetBlockByHash returns a block
    39  // - GetTransactionByID returns a transaction
    40  type LedgerQuerier struct {
    41  	policyChecker policy.PolicyChecker
    42  }
    43  
    44  var qscclogger = flogging.MustGetLogger("qscc")
    45  
    46  // These are function names from Invoke first parameter
    47  const (
    48  	GetChainInfo       string = "GetChainInfo"
    49  	GetBlockByNumber   string = "GetBlockByNumber"
    50  	GetBlockByHash     string = "GetBlockByHash"
    51  	GetTransactionByID string = "GetTransactionByID"
    52  	GetBlockByTxID     string = "GetBlockByTxID"
    53  	GetBlockWithHashByNumber   string = "GetBlockWithHashByNumber"
    54  )
    55  
    56  // Init is called once per chain when the chain is created.
    57  // This allows the chaincode to initialize any variables on the ledger prior
    58  // to any transaction execution on the chain.
    59  func (e *LedgerQuerier) Init(stub shim.ChaincodeStubInterface) pb.Response {
    60  	qscclogger.Info("Init QSCC")
    61  
    62  	// Init policy checker for access control
    63  	e.policyChecker = policy.NewPolicyChecker(
    64  		peer.NewChannelPolicyManagerGetter(),
    65  		mgmt.GetLocalMSP(),
    66  		mgmt.NewLocalMSPPrincipalGetter(),
    67  	)
    68  
    69  	return shim.Success(nil)
    70  }
    71  
    72  // Invoke is called with args[0] contains the query function name, args[1]
    73  // contains the chain ID, which is temporary for now until it is part of stub.
    74  // Each function requires additional parameters as described below:
    75  // # GetChainInfo: Return a BlockchainInfo object marshalled in bytes
    76  // # GetBlockByNumber: Return the block specified by block number in args[2]
    77  // # GetBlockByHash: Return the block specified by block hash in args[2]
    78  // # GetTransactionByID: Return the transaction specified by ID in args[2]
    79  func (e *LedgerQuerier) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
    80  	args := stub.GetArgs()
    81  
    82  	if len(args) < 2 {
    83  		return shim.Error(fmt.Sprintf("Incorrect number of arguments, %d", len(args)))
    84  	}
    85  	fname := string(args[0])
    86  	cid := string(args[1])
    87  
    88  	if fname != GetChainInfo && len(args) < 3 {
    89  		return shim.Error(fmt.Sprintf("missing 3rd argument for %s", fname))
    90  	}
    91  
    92  	targetLedger := peer.GetLedger(cid)
    93  	if targetLedger == nil {
    94  		return shim.Error(fmt.Sprintf("Invalid chain ID, %s", cid))
    95  	}
    96  
    97  	qscclogger.Debugf("Invoke function: %s on chain: %s", fname, cid)
    98  
    99  	// Handle ACL:
   100  	// 1. get the signed proposal
   101  	sp, err := stub.GetSignedProposal()
   102  	if err != nil {
   103  		return shim.Error(fmt.Sprintf("Failed getting signed proposal from stub, %s: %s", cid, err))
   104  	}
   105  
   106  	// 2. check the channel reader policy
   107  	if err = e.policyChecker.CheckPolicy(cid, policies.ChannelApplicationReaders, sp); err != nil {
   108  		return shim.Error(fmt.Sprintf("Authorization request failed %s: %s", cid, err))
   109  	}
   110  
   111  	switch fname {
   112  	case GetTransactionByID:
   113  		return getTransactionByID(targetLedger, args[2])
   114  	case GetBlockByNumber:
   115  		return getBlockByNumber(targetLedger, args[2])
   116  	case GetBlockByHash:
   117  		return getBlockByHash(targetLedger, args[2])
   118  	case GetChainInfo:
   119  		return getChainInfo(targetLedger)
   120  	case GetBlockByTxID:
   121  		return getBlockByTxID(targetLedger, args[2])
   122  	case GetBlockWithHashByNumber:
   123  		return getBlockWithHashByNumber(targetLedger, args[2])
   124  	}
   125  
   126  	return shim.Error(fmt.Sprintf("Requested function %s not found.", fname))
   127  }
   128  
   129  func getTransactionByID(vledger ledger.PeerLedger, tid []byte) pb.Response {
   130  	if tid == nil {
   131  		return shim.Error("Transaction ID must not be nil.")
   132  	}
   133  
   134  	processedTran, err := vledger.GetTransactionByID(string(tid))
   135  	if err != nil {
   136  		return shim.Error(fmt.Sprintf("Failed to get transaction with id %s, error %s", string(tid), err))
   137  	}
   138  
   139  	bytes, err := utils.Marshal(processedTran)
   140  	if err != nil {
   141  		return shim.Error(err.Error())
   142  	}
   143  
   144  	return shim.Success(bytes)
   145  }
   146  
   147  func getBlockByNumber(vledger ledger.PeerLedger, number []byte) pb.Response {
   148  	if number == nil {
   149  		return shim.Error("Block number must not be nil.")
   150  	}
   151  	bnum, err := strconv.ParseUint(string(number), 10, 64)
   152  	if err != nil {
   153  		return shim.Error(fmt.Sprintf("Failed to parse block number with error %s", err))
   154  	}
   155  	block, err := vledger.GetBlockByNumber(bnum)
   156  	if err != nil {
   157  		return shim.Error(fmt.Sprintf("Failed to get block number %d, error %s", bnum, err))
   158  	}
   159  	// TODO: consider trim block content before returning
   160  	//  Specifically, trim transaction 'data' out of the transaction array Payloads
   161  	//  This will preserve the transaction Payload header,
   162  	//  and client can do GetTransactionByID() if they want the full transaction details
   163  
   164  	bytes, err := utils.Marshal(block)
   165  	if err != nil {
   166  		return shim.Error(err.Error())
   167  	}
   168  
   169  	return shim.Success(bytes)
   170  }
   171  
   172  func getBlockByHash(vledger ledger.PeerLedger, hash []byte) pb.Response {
   173  	if hash == nil {
   174  		return shim.Error("Block hash must not be nil.")
   175  	}
   176  	block, err := vledger.GetBlockByHash(hash)
   177  	if err != nil {
   178  		return shim.Error(fmt.Sprintf("Failed to get block hash %s, error %s", string(hash), err))
   179  	}
   180  	// TODO: consider trim block content before returning
   181  	//  Specifically, trim transaction 'data' out of the transaction array Payloads
   182  	//  This will preserve the transaction Payload header,
   183  	//  and client can do GetTransactionByID() if they want the full transaction details
   184  
   185  	bytes, err := utils.Marshal(block)
   186  	if err != nil {
   187  		return shim.Error(err.Error())
   188  	}
   189  
   190  	return shim.Success(bytes)
   191  }
   192  
   193  func getBlockWithHashByNumber(vledger ledger.PeerLedger, number []byte) pb.Response {
   194  	if number == nil {
   195  		return shim.Error("Block number must not be nil.")
   196  	}
   197  	bnum, err := strconv.ParseUint(string(number), 10, 64)
   198  	if err != nil {
   199  		return shim.Error(fmt.Sprintf("Failed to parse block number with error %s", err))
   200  	}
   201  	processedBlock, err := vledger.GetBlockWithHashByNumber(bnum)
   202  	if err != nil {
   203  		return shim.Error(fmt.Sprintf("Failed to get block number %d, error %s", bnum, err))
   204  	}
   205  	bytes, err := utils.Marshal(processedBlock)
   206  	if err != nil {
   207  		return shim.Error(err.Error())
   208  	}
   209  
   210  	return shim.Success(bytes)
   211  }
   212  
   213  func getChainInfo(vledger ledger.PeerLedger) pb.Response {
   214  	binfo, err := vledger.GetBlockchainInfo()
   215  	if err != nil {
   216  		return shim.Error(fmt.Sprintf("Failed to get block info with error %s", err))
   217  	}
   218  	bytes, err := utils.Marshal(binfo)
   219  	if err != nil {
   220  		return shim.Error(err.Error())
   221  	}
   222  
   223  	return shim.Success(bytes)
   224  }
   225  
   226  func getBlockByTxID(vledger ledger.PeerLedger, rawTxID []byte) pb.Response {
   227  	txID := string(rawTxID)
   228  	block, err := vledger.GetBlockByTxID(txID)
   229  
   230  	if err != nil {
   231  		return shim.Error(fmt.Sprintf("Failed to get block for txID %s, error %s", txID, err))
   232  	}
   233  
   234  	bytes, err := utils.Marshal(block)
   235  
   236  	if err != nil {
   237  		return shim.Error(err.Error())
   238  	}
   239  
   240  	return shim.Success(bytes)
   241  }