github.com/kaituanwang/hyperledger@v2.0.1+incompatible/core/scc/qscc/query.go (about) 1 /* 2 Copyright IBM Corp. 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/hyperledger/fabric-chaincode-go/shim" 14 pb "github.com/hyperledger/fabric-protos-go/peer" 15 "github.com/hyperledger/fabric/common/flogging" 16 "github.com/hyperledger/fabric/core/aclmgmt" 17 "github.com/hyperledger/fabric/core/ledger" 18 "github.com/hyperledger/fabric/protoutil" 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 213 if err != nil { 214 return shim.Error(fmt.Sprintf("Failed to get block for txID %s, error %s", txID, err)) 215 } 216 217 bytes, err := protoutil.Marshal(block) 218 219 if err != nil { 220 return shim.Error(err.Error()) 221 } 222 223 return shim.Success(bytes) 224 } 225 226 func getACLResource(fname string) string { 227 return "qscc/" + fname 228 }