github.com/leonlxy/hyperledger@v1.0.0-alpha.0.20170427033203-34922035d248/core/chaincode/shim/mockstub.go (about) 1 /* 2 Copyright IBM Corp. 2016 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 shim provides APIs for the chaincode to access its state 18 // variables, transaction context and call other chaincodes. 19 package shim 20 21 import ( 22 "container/list" 23 "errors" 24 "fmt" 25 "strings" 26 27 "github.com/golang/protobuf/ptypes/timestamp" 28 "github.com/hyperledger/fabric/common/util" 29 "github.com/hyperledger/fabric/protos/ledger/queryresult" 30 pb "github.com/hyperledger/fabric/protos/peer" 31 "github.com/op/go-logging" 32 ) 33 34 // Logger for the shim package. 35 var mockLogger = logging.MustGetLogger("mock") 36 37 // MockStub is an implementation of ChaincodeStubInterface for unit testing chaincode. 38 // Use this instead of ChaincodeStub in your chaincode's unit test calls to Init or Invoke. 39 type MockStub struct { 40 // arguments the stub was called with 41 args [][]byte 42 43 // A pointer back to the chaincode that will invoke this, set by constructor. 44 // If a peer calls this stub, the chaincode will be invoked from here. 45 cc Chaincode 46 47 // A nice name that can be used for logging 48 Name string 49 50 // State keeps name value pairs 51 State map[string][]byte 52 53 // Keys stores the list of mapped values in lexical order 54 Keys *list.List 55 56 // registered list of other MockStub chaincodes that can be called from this MockStub 57 Invokables map[string]*MockStub 58 59 // stores a transaction uuid while being Invoked / Deployed 60 // TODO if a chaincode uses recursion this may need to be a stack of TxIDs or possibly a reference counting map 61 TxID string 62 63 TxTimestamp *timestamp.Timestamp 64 65 // mocked signedProposal 66 signedProposal *pb.SignedProposal 67 } 68 69 func (stub *MockStub) GetTxID() string { 70 return stub.TxID 71 } 72 73 func (stub *MockStub) GetArgs() [][]byte { 74 return stub.args 75 } 76 77 func (stub *MockStub) GetStringArgs() []string { 78 args := stub.GetArgs() 79 strargs := make([]string, 0, len(args)) 80 for _, barg := range args { 81 strargs = append(strargs, string(barg)) 82 } 83 return strargs 84 } 85 86 func (stub *MockStub) GetFunctionAndParameters() (function string, params []string) { 87 allargs := stub.GetStringArgs() 88 function = "" 89 params = []string{} 90 if len(allargs) >= 1 { 91 function = allargs[0] 92 params = allargs[1:] 93 } 94 return 95 } 96 97 // Used to indicate to a chaincode that it is part of a transaction. 98 // This is important when chaincodes invoke each other. 99 // MockStub doesn't support concurrent transactions at present. 100 func (stub *MockStub) MockTransactionStart(txid string) { 101 stub.TxID = txid 102 stub.setSignedProposal(&pb.SignedProposal{}) 103 stub.setTxTimestamp(util.CreateUtcTimestamp()) 104 } 105 106 // End a mocked transaction, clearing the UUID. 107 func (stub *MockStub) MockTransactionEnd(uuid string) { 108 stub.signedProposal = nil 109 stub.TxID = "" 110 } 111 112 // Register a peer chaincode with this MockStub 113 // invokableChaincodeName is the name or hash of the peer 114 // otherStub is a MockStub of the peer, already intialised 115 func (stub *MockStub) MockPeerChaincode(invokableChaincodeName string, otherStub *MockStub) { 116 stub.Invokables[invokableChaincodeName] = otherStub 117 } 118 119 // Initialise this chaincode, also starts and ends a transaction. 120 func (stub *MockStub) MockInit(uuid string, args [][]byte) pb.Response { 121 stub.args = args 122 stub.MockTransactionStart(uuid) 123 res := stub.cc.Init(stub) 124 stub.MockTransactionEnd(uuid) 125 return res 126 } 127 128 // Invoke this chaincode, also starts and ends a transaction. 129 func (stub *MockStub) MockInvoke(uuid string, args [][]byte) pb.Response { 130 stub.args = args 131 stub.MockTransactionStart(uuid) 132 res := stub.cc.Invoke(stub) 133 stub.MockTransactionEnd(uuid) 134 return res 135 } 136 137 // Invoke this chaincode, also starts and ends a transaction. 138 func (stub *MockStub) MockInvokeWithSignedProposal(uuid string, args [][]byte, sp *pb.SignedProposal) pb.Response { 139 stub.args = args 140 stub.MockTransactionStart(uuid) 141 stub.signedProposal = sp 142 res := stub.cc.Invoke(stub) 143 stub.MockTransactionEnd(uuid) 144 return res 145 } 146 147 // GetState retrieves the value for a given key from the ledger 148 func (stub *MockStub) GetState(key string) ([]byte, error) { 149 value := stub.State[key] 150 mockLogger.Debug("MockStub", stub.Name, "Getting", key, value) 151 return value, nil 152 } 153 154 // PutState writes the specified `value` and `key` into the ledger. 155 func (stub *MockStub) PutState(key string, value []byte) error { 156 if stub.TxID == "" { 157 mockLogger.Error("Cannot PutState without a transactions - call stub.MockTransactionStart()?") 158 return errors.New("Cannot PutState without a transactions - call stub.MockTransactionStart()?") 159 } 160 161 mockLogger.Debug("MockStub", stub.Name, "Putting", key, value) 162 stub.State[key] = value 163 164 // insert key into ordered list of keys 165 for elem := stub.Keys.Front(); elem != nil; elem = elem.Next() { 166 elemValue := elem.Value.(string) 167 comp := strings.Compare(key, elemValue) 168 mockLogger.Debug("MockStub", stub.Name, "Compared", key, elemValue, " and got ", comp) 169 if comp < 0 { 170 // key < elem, insert it before elem 171 stub.Keys.InsertBefore(key, elem) 172 mockLogger.Debug("MockStub", stub.Name, "Key", key, " inserted before", elem.Value) 173 break 174 } else if comp == 0 { 175 // keys exists, no need to change 176 mockLogger.Debug("MockStub", stub.Name, "Key", key, "already in State") 177 break 178 } else { // comp > 0 179 // key > elem, keep looking unless this is the end of the list 180 if elem.Next() == nil { 181 stub.Keys.PushBack(key) 182 mockLogger.Debug("MockStub", stub.Name, "Key", key, "appended") 183 break 184 } 185 } 186 } 187 188 // special case for empty Keys list 189 if stub.Keys.Len() == 0 { 190 stub.Keys.PushFront(key) 191 mockLogger.Debug("MockStub", stub.Name, "Key", key, "is first element in list") 192 } 193 194 return nil 195 } 196 197 // DelState removes the specified `key` and its value from the ledger. 198 func (stub *MockStub) DelState(key string) error { 199 mockLogger.Debug("MockStub", stub.Name, "Deleting", key, stub.State[key]) 200 delete(stub.State, key) 201 202 for elem := stub.Keys.Front(); elem != nil; elem = elem.Next() { 203 if strings.Compare(key, elem.Value.(string)) == 0 { 204 stub.Keys.Remove(elem) 205 } 206 } 207 208 return nil 209 } 210 211 func (stub *MockStub) GetStateByRange(startKey, endKey string) (StateQueryIteratorInterface, error) { 212 return NewMockStateRangeQueryIterator(stub, startKey, endKey), nil 213 } 214 215 // GetQueryResult function can be invoked by a chaincode to perform a 216 // rich query against state database. Only supported by state database implementations 217 // that support rich query. The query string is in the syntax of the underlying 218 // state database. An iterator is returned which can be used to iterate (next) over 219 // the query result set 220 func (stub *MockStub) GetQueryResult(query string) (StateQueryIteratorInterface, error) { 221 // Not implemented since the mock engine does not have a query engine. 222 // However, a very simple query engine that supports string matching 223 // could be implemented to test that the framework supports queries 224 return nil, errors.New("Not Implemented") 225 } 226 227 // GetHistoryForKey function can be invoked by a chaincode to return a history of 228 // key values across time. GetHistoryForKey is intended to be used for read-only queries. 229 func (stub *MockStub) GetHistoryForKey(key string) (HistoryQueryIteratorInterface, error) { 230 return nil, errors.New("Not Implemented") 231 } 232 233 //GetStateByPartialCompositeKey function can be invoked by a chaincode to query the 234 //state based on a given partial composite key. This function returns an 235 //iterator which can be used to iterate over all composite keys whose prefix 236 //matches the given partial composite key. This function should be used only for 237 //a partial composite key. For a full composite key, an iter with empty response 238 //would be returned. 239 func (stub *MockStub) GetStateByPartialCompositeKey(objectType string, attributes []string) (StateQueryIteratorInterface, error) { 240 return getStateByPartialCompositeKey(stub, objectType, attributes) 241 } 242 243 // CreateCompositeKey combines the list of attributes 244 //to form a composite key. 245 func (stub *MockStub) CreateCompositeKey(objectType string, attributes []string) (string, error) { 246 return createCompositeKey(objectType, attributes) 247 } 248 249 // SplitCompositeKey splits the composite key into attributes 250 // on which the composite key was formed. 251 func (stub *MockStub) SplitCompositeKey(compositeKey string) (string, []string, error) { 252 return splitCompositeKey(compositeKey) 253 } 254 255 // InvokeChaincode calls a peered chaincode. 256 // E.g. stub1.InvokeChaincode("stub2Hash", funcArgs, channel) 257 // Before calling this make sure to create another MockStub stub2, call stub2.MockInit(uuid, func, args) 258 // and register it with stub1 by calling stub1.MockPeerChaincode("stub2Hash", stub2) 259 func (stub *MockStub) InvokeChaincode(chaincodeName string, args [][]byte, channel string) pb.Response { 260 // Internally we use chaincode name as a composite name 261 if channel != "" { 262 chaincodeName = chaincodeName + "/" + channel 263 } 264 // TODO "args" here should possibly be a serialized pb.ChaincodeInput 265 otherStub := stub.Invokables[chaincodeName] 266 mockLogger.Debug("MockStub", stub.Name, "Invoking peer chaincode", otherStub.Name, args) 267 // function, strings := getFuncArgs(args) 268 res := otherStub.MockInvoke(stub.TxID, args) 269 mockLogger.Debug("MockStub", stub.Name, "Invoked peer chaincode", otherStub.Name, "got", fmt.Sprintf("%+v", res)) 270 return res 271 } 272 273 // Not implemented 274 func (stub *MockStub) GetCreator() ([]byte, error) { 275 return nil, nil 276 } 277 278 // Not implemented 279 func (stub *MockStub) GetTransient() (map[string][]byte, error) { 280 return nil, nil 281 } 282 283 // Not implemented 284 func (stub *MockStub) GetBinding() ([]byte, error) { 285 return nil, nil 286 } 287 288 // Not implemented 289 func (stub *MockStub) GetSignedProposal() (*pb.SignedProposal, error) { 290 return stub.signedProposal, nil 291 } 292 293 func (stub *MockStub) setSignedProposal(sp *pb.SignedProposal) { 294 stub.signedProposal = sp 295 } 296 297 // Not implemented 298 func (stub *MockStub) GetArgsSlice() ([]byte, error) { 299 return nil, nil 300 } 301 302 func (stub *MockStub) setTxTimestamp(time *timestamp.Timestamp) { 303 stub.TxTimestamp = time 304 } 305 306 func (stub *MockStub) GetTxTimestamp() (*timestamp.Timestamp, error) { 307 if stub.TxTimestamp == nil { 308 return nil, errors.New("TxTimestamp not set.") 309 } 310 return stub.TxTimestamp, nil 311 } 312 313 // Not implemented 314 func (stub *MockStub) SetEvent(name string, payload []byte) error { 315 return nil 316 } 317 318 // Constructor to initialise the internal State map 319 func NewMockStub(name string, cc Chaincode) *MockStub { 320 mockLogger.Debug("MockStub(", name, cc, ")") 321 s := new(MockStub) 322 s.Name = name 323 s.cc = cc 324 s.State = make(map[string][]byte) 325 s.Invokables = make(map[string]*MockStub) 326 s.Keys = list.New() 327 328 return s 329 } 330 331 /***************************** 332 Range Query Iterator 333 *****************************/ 334 335 type MockStateRangeQueryIterator struct { 336 Closed bool 337 Stub *MockStub 338 StartKey string 339 EndKey string 340 Current *list.Element 341 } 342 343 // HasNext returns true if the range query iterator contains additional keys 344 // and values. 345 func (iter *MockStateRangeQueryIterator) HasNext() bool { 346 if iter.Closed { 347 // previously called Close() 348 mockLogger.Error("HasNext() but already closed") 349 return false 350 } 351 352 if iter.Current == nil { 353 mockLogger.Error("HasNext() couldn't get Current") 354 return false 355 } 356 357 current := iter.Current 358 for current != nil { 359 // if this is an open-ended query for all keys, return true 360 if iter.StartKey == "" && iter.EndKey == "" { 361 return true 362 } 363 comp1 := strings.Compare(current.Value.(string), iter.StartKey) 364 comp2 := strings.Compare(current.Value.(string), iter.EndKey) 365 if comp1 >= 0 { 366 if comp2 <= 0 { 367 mockLogger.Debug("HasNext() got next") 368 return true 369 } else { 370 mockLogger.Debug("HasNext() but no next") 371 return false 372 373 } 374 } 375 current = current.Next() 376 } 377 378 // we've reached the end of the underlying values 379 mockLogger.Debug("HasNext() but no next") 380 return false 381 } 382 383 // Next returns the next key and value in the range query iterator. 384 func (iter *MockStateRangeQueryIterator) Next() (*queryresult.KV, error) { 385 if iter.Closed == true { 386 mockLogger.Error("MockStateRangeQueryIterator.Next() called after Close()") 387 return nil, errors.New("MockStateRangeQueryIterator.Next() called after Close()") 388 } 389 390 if iter.HasNext() == false { 391 mockLogger.Error("MockStateRangeQueryIterator.Next() called when it does not HaveNext()") 392 return nil, errors.New("MockStateRangeQueryIterator.Next() called when it does not HaveNext()") 393 } 394 395 for iter.Current != nil { 396 comp1 := strings.Compare(iter.Current.Value.(string), iter.StartKey) 397 comp2 := strings.Compare(iter.Current.Value.(string), iter.EndKey) 398 // compare to start and end keys. or, if this is an open-ended query for 399 // all keys, it should always return the key and value 400 if (comp1 >= 0 && comp2 <= 0) || (iter.StartKey == "" && iter.EndKey == "") { 401 key := iter.Current.Value.(string) 402 value, err := iter.Stub.GetState(key) 403 iter.Current = iter.Current.Next() 404 return &queryresult.KV{Key: key, Value: value}, err 405 } 406 iter.Current = iter.Current.Next() 407 } 408 mockLogger.Error("MockStateRangeQueryIterator.Next() went past end of range") 409 return nil, errors.New("MockStateRangeQueryIterator.Next() went past end of range") 410 } 411 412 // Close closes the range query iterator. This should be called when done 413 // reading from the iterator to free up resources. 414 func (iter *MockStateRangeQueryIterator) Close() error { 415 if iter.Closed == true { 416 mockLogger.Error("MockStateRangeQueryIterator.Close() called after Close()") 417 return errors.New("MockStateRangeQueryIterator.Close() called after Close()") 418 } 419 420 iter.Closed = true 421 return nil 422 } 423 424 func (iter *MockStateRangeQueryIterator) Print() { 425 mockLogger.Debug("MockStateRangeQueryIterator {") 426 mockLogger.Debug("Closed?", iter.Closed) 427 mockLogger.Debug("Stub", iter.Stub) 428 mockLogger.Debug("StartKey", iter.StartKey) 429 mockLogger.Debug("EndKey", iter.EndKey) 430 mockLogger.Debug("Current", iter.Current) 431 mockLogger.Debug("HasNext?", iter.HasNext()) 432 mockLogger.Debug("}") 433 } 434 435 func NewMockStateRangeQueryIterator(stub *MockStub, startKey string, endKey string) *MockStateRangeQueryIterator { 436 mockLogger.Debug("NewMockStateRangeQueryIterator(", stub, startKey, endKey, ")") 437 iter := new(MockStateRangeQueryIterator) 438 iter.Closed = false 439 iter.Stub = stub 440 iter.StartKey = startKey 441 iter.EndKey = endKey 442 iter.Current = stub.Keys.Front() 443 444 iter.Print() 445 446 return iter 447 } 448 449 func getBytes(function string, args []string) [][]byte { 450 bytes := make([][]byte, 0, len(args)+1) 451 bytes = append(bytes, []byte(function)) 452 for _, s := range args { 453 bytes = append(bytes, []byte(s)) 454 } 455 return bytes 456 } 457 458 func getFuncArgs(bytes [][]byte) (string, []string) { 459 mockLogger.Debugf("getFuncArgs(%x)", bytes) 460 function := string(bytes[0]) 461 args := make([]string, len(bytes)-1) 462 for i := 1; i < len(bytes); i++ { 463 mockLogger.Debugf("getFuncArgs - i:%x, len(bytes):%x", i, len(bytes)) 464 args[i-1] = string(bytes[i]) 465 } 466 return function, args 467 }