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 }