github.com/s7techlab/cckit@v0.10.5/testing/mockstub.go (about) 1 package testing 2 3 import ( 4 "container/list" 5 "crypto/rand" 6 "fmt" 7 "strings" 8 "sync" 9 "unicode/utf8" 10 11 "github.com/hyperledger/fabric-chaincode-go/shim" 12 "github.com/hyperledger/fabric-chaincode-go/shimtest" 13 "github.com/hyperledger/fabric-protos-go/ledger/queryresult" 14 "github.com/hyperledger/fabric-protos-go/peer" 15 "github.com/hyperledger/fabric/msp" 16 "github.com/pkg/errors" 17 18 "github.com/s7techlab/cckit/convert" 19 ) 20 21 const EventChannelBufferSize = 100 22 23 var ( 24 // ErrChaincodeNotExists occurs when attempting to invoke a nonexostent external chaincode 25 ErrChaincodeNotExists = errors.New(`chaincode not exists`) 26 // ErrUnknownFromArgsType occurs when attempting to set unknown args in From func 27 ErrUnknownFromArgsType = errors.New(`unknown args type to cckit.MockStub.From func`) 28 // ErrKeyAlreadyExistsInTransientMap occurs when attempting to set existing key in transient map 29 ErrKeyAlreadyExistsInTransientMap = errors.New(`key already exists in transient map`) 30 ) 31 32 type StateItem struct { 33 Key string 34 Value []byte 35 Delete bool 36 } 37 38 // MockStub replacement of shim.MockStub with creator mocking facilities 39 type MockStub struct { 40 shimtest.MockStub 41 cc shim.Chaincode 42 43 StateBuffer []*StateItem // buffer for state changes during transaction 44 45 m sync.Mutex 46 47 _args [][]byte 48 transient map[string][]byte 49 mockCreator []byte 50 TxResult peer.Response // last tx result 51 52 ClearCreatorAfterInvoke bool 53 creatorTransformer CreatorTransformer // transformer for tx creator data, used in From func 54 55 Invokables map[string]*MockStub // invokable this version of MockStub 56 57 LastTxID string 58 ChaincodeEvent *peer.ChaincodeEvent // event in last tx 59 chaincodeEventSubscriptions []chan *peer.ChaincodeEvent // multiple event subscriptions 60 61 PrivateKeys map[string]*list.List 62 // flag for cc2cc invokation via InvokeChaincode to dump state on outer tx finish 63 // https://github.com/s7techlab/cckit/issues/97 64 cc2ccInvokation bool 65 } 66 67 type CreatorTransformer func(...interface{}) (mspID string, certPEM []byte, err error) 68 69 // NewMockStub creates chaincode imitation 70 func NewMockStub(name string, cc shim.Chaincode) *MockStub { 71 return &MockStub{ 72 MockStub: *shimtest.NewMockStub(name, cc), 73 cc: cc, 74 // by default tx creator data and transient map are cleared after each cc method query/invoke 75 ClearCreatorAfterInvoke: true, 76 Invokables: make(map[string]*MockStub), 77 PrivateKeys: make(map[string]*list.List), 78 } 79 } 80 81 // PutState wrapped functions puts state items in queue and dumps 82 // to state after invocation 83 func (stub *MockStub) PutState(key string, value []byte) error { 84 if stub.TxID == "" { 85 return errors.New("cannot PutState without a transactions - call stub.MockTransactionStart()?") 86 } 87 stub.StateBuffer = append(stub.StateBuffer, &StateItem{ 88 Key: key, 89 Value: value, 90 }) 91 92 return nil 93 } 94 95 func (stub *MockStub) DelState(key string) error { 96 if stub.TxID == "" { 97 return errors.New("cannot PutState without a transactions - call stub.MockTransactionStart()?") 98 } 99 stub.StateBuffer = append(stub.StateBuffer, &StateItem{ 100 Key: key, 101 Delete: true, 102 }) 103 104 return nil 105 } 106 107 // GetArgs mocked args 108 func (stub *MockStub) GetArgs() [][]byte { 109 return stub._args 110 } 111 112 // SetArgs set mocked args 113 func (stub *MockStub) SetArgs(args [][]byte) { 114 stub._args = args 115 } 116 117 // SetEvent sets chaincode event 118 func (stub *MockStub) SetEvent(name string, payload []byte) error { 119 if name == "" { 120 return errors.New("event name can not be nil string") 121 } 122 123 stub.ChaincodeEvent = &peer.ChaincodeEvent{ 124 ChaincodeId: stub.Name, 125 TxId: stub.TxID, 126 EventName: name, 127 Payload: payload, 128 } 129 return nil 130 } 131 132 // EventSubscription for new or all events 133 func (stub *MockStub) EventSubscription(from ...int64) (events chan *peer.ChaincodeEvent, closer func() error) { 134 stub.m.Lock() 135 defer stub.m.Unlock() 136 137 events = make(chan *peer.ChaincodeEvent, EventChannelBufferSize) 138 139 if len(from) > 0 && from[0] == 0 { 140 curLen := len(stub.ChaincodeEventsChannel) 141 142 for i := 0; i < curLen; i++ { 143 e := <-stub.ChaincodeEventsChannel 144 events <- e 145 stub.ChaincodeEventsChannel <- e 146 } 147 } 148 149 stub.chaincodeEventSubscriptions = append(stub.chaincodeEventSubscriptions, events) 150 151 subPos := len(stub.chaincodeEventSubscriptions) - 1 152 return events, func() error { 153 stub.m.Lock() 154 defer stub.m.Unlock() 155 156 if stub.chaincodeEventSubscriptions[subPos] != nil { 157 close(stub.chaincodeEventSubscriptions[subPos]) 158 stub.chaincodeEventSubscriptions[subPos] = nil 159 } 160 161 return nil 162 } 163 } 164 165 func (stub *MockStub) EventsList() []*peer.ChaincodeEvent { 166 stub.m.Lock() 167 defer stub.m.Unlock() 168 169 var eventsList []*peer.ChaincodeEvent 170 171 curLen := len(stub.ChaincodeEventsChannel) 172 173 for i := 0; i < curLen; i++ { 174 e := <-stub.ChaincodeEventsChannel 175 eventsList = append(eventsList, e) 176 stub.ChaincodeEventsChannel <- e 177 } 178 179 return eventsList 180 } 181 182 // ClearEvents clears chaincode events channel 183 func (stub *MockStub) ClearEvents() { 184 for len(stub.ChaincodeEventsChannel) > 0 { 185 <-stub.ChaincodeEventsChannel 186 } 187 } 188 189 // GetStringArgs get mocked args as strings 190 func (stub *MockStub) GetStringArgs() []string { 191 args := stub.GetArgs() 192 strargs := make([]string, 0, len(args)) 193 for _, barg := range args { 194 strargs = append(strargs, string(barg)) 195 } 196 return strargs 197 } 198 199 // MockPeerChaincode link to another MockStub 200 func (stub *MockStub) MockPeerChaincode(invokableChaincodeName string, otherStub *MockStub) { 201 stub.Invokables[invokableChaincodeName] = otherStub 202 } 203 204 // MockedPeerChaincodes returns names of mocked chaincodes, available for invoke from current stub 205 func (stub *MockStub) MockedPeerChaincodes() []string { 206 keys := make([]string, 0) 207 for k := range stub.Invokables { 208 keys = append(keys, k) 209 } 210 return keys 211 } 212 213 // InvokeChaincode using another MockStub 214 func (stub *MockStub) InvokeChaincode(chaincodeName string, args [][]byte, channel string) peer.Response { 215 // Internally we use chaincode name as a composite name 216 ccName := chaincodeName 217 if channel != "" { 218 chaincodeName = chaincodeName + "/" + channel 219 } 220 221 otherStub, exists := stub.Invokables[chaincodeName] 222 if !exists { 223 return shim.Error(fmt.Sprintf( 224 `%s : try to invoke chaincode "%s" in channel "%s" (%s). Available mocked chaincodes are: %s`, 225 ErrChaincodeNotExists, ccName, channel, chaincodeName, stub.MockedPeerChaincodes())) 226 } 227 228 otherStub.mockCreator = stub.mockCreator 229 otherStub.cc2ccInvokation = true 230 res := otherStub.MockInvoke(stub.TxID, args) 231 return res 232 } 233 234 // GetFunctionAndParameters mocked 235 func (stub *MockStub) GetFunctionAndParameters() (function string, params []string) { 236 allargs := stub.GetStringArgs() 237 function = "" 238 params = []string{} 239 if len(allargs) >= 1 { 240 function = allargs[0] 241 params = allargs[1:] 242 } 243 return 244 } 245 246 // RegisterCreatorTransformer that transforms creator data to MSP_ID and X.509 certificate 247 func (stub *MockStub) RegisterCreatorTransformer(creatorTransformer CreatorTransformer) *MockStub { 248 stub.creatorTransformer = creatorTransformer 249 return stub 250 } 251 252 // MockCreator of tx 253 func (stub *MockStub) MockCreator(mspID string, certPEM []byte) { 254 stub.mockCreator, _ = msp.NewSerializedIdentity(mspID, certPEM) 255 } 256 257 func (stub *MockStub) generateTxUID() string { 258 id := make([]byte, 32) 259 if _, err := rand.Read(id); err != nil { 260 panic(err) 261 } 262 return fmt.Sprintf("0x%x", id) 263 } 264 265 // Init func of chaincode - sugared version with autogenerated tx uuid 266 func (stub *MockStub) Init(iargs ...interface{}) peer.Response { 267 args, err := convert.ArgsToBytes(iargs...) 268 if err != nil { 269 return shim.Error(err.Error()) 270 } 271 272 return stub.MockInit(stub.generateTxUID(), args) 273 } 274 275 // InitBytes init func with ...[]byte args 276 func (stub *MockStub) InitBytes(args ...[]byte) peer.Response { 277 return stub.MockInit(stub.generateTxUID(), args) 278 } 279 280 // MockInit mocked init function 281 func (stub *MockStub) MockInit(uuid string, args [][]byte) peer.Response { 282 stub.m.Lock() 283 defer stub.m.Unlock() 284 285 stub.SetArgs(args) 286 287 stub.MockTransactionStart(uuid) 288 stub.TxResult = stub.cc.Init(stub) 289 stub.MockTransactionEnd(uuid) 290 291 return stub.TxResult 292 } 293 294 func (stub *MockStub) DumpStateBuffer() { 295 // dump state buffer to state 296 if stub.TxResult.Status == shim.OK { 297 for i := range stub.StateBuffer { 298 s := stub.StateBuffer[i] 299 if s.Delete { 300 _ = stub.MockStub.DelState(s.Key) 301 } else { 302 _ = stub.MockStub.PutState(s.Key, s.Value) 303 } 304 } 305 } else { 306 stub.ChaincodeEvent = nil 307 } 308 stub.StateBuffer = nil 309 } 310 311 func (stub *MockStub) dumpEvents() { 312 if stub.ChaincodeEvent != nil { 313 // send only last event 314 for _, sub := range stub.chaincodeEventSubscriptions { 315 // subscription can be closed 316 if sub != nil { 317 sub <- stub.ChaincodeEvent 318 } 319 } 320 stub.ChaincodeEventsChannel <- stub.ChaincodeEvent 321 } 322 } 323 324 // MockQuery 325 func (stub *MockStub) MockQuery(uuid string, args [][]byte) peer.Response { 326 return stub.MockInvoke(uuid, args) 327 } 328 329 func (stub *MockStub) MockTransactionStart(uuid string) { 330 //empty event 331 stub.ChaincodeEvent = nil 332 // empty state buffer 333 stub.StateBuffer = nil 334 stub.TxResult = peer.Response{} 335 336 stub.MockStub.MockTransactionStart(uuid) 337 } 338 339 func (stub *MockStub) MockTransactionEnd(uuid string) { 340 stub.LastTxID = stub.TxID 341 if !stub.cc2ccInvokation { // skip for inner tx cc2cc calls 342 stub.DumpStateBuffer() 343 stub.dumpEvents() // events works only for outer stub in Fabric 344 345 // dump buffer to state on outer tx finishing (https://github.com/s7techlab/cckit/issues/97) 346 for _, invokableStub := range stub.Invokables { 347 invokableStub.DumpStateBuffer() 348 invokableStub.cc2ccInvokation = false 349 } 350 stub.MockStub.MockTransactionEnd(uuid) 351 } 352 353 if stub.ClearCreatorAfterInvoke { 354 stub.mockCreator = nil 355 stub.transient = nil 356 } 357 } 358 359 // MockInvoke 360 func (stub *MockStub) MockInvoke(uuid string, args [][]byte) peer.Response { 361 stub.m.Lock() 362 defer stub.m.Unlock() 363 364 // this is a hack here to set MockStub.args, because its not accessible otherwise 365 stub.SetArgs(args) 366 367 // now do the invoke with the correct stub 368 stub.MockTransactionStart(uuid) 369 stub.TxResult = stub.cc.Invoke(stub) 370 stub.MockTransactionEnd(uuid) 371 372 return stub.TxResult 373 } 374 375 // Invoke sugared invoke function with autogenerated tx uuid 376 func (stub *MockStub) Invoke(funcName string, iargs ...interface{}) peer.Response { 377 fargs, err := convert.ArgsToBytes(iargs...) 378 if err != nil { 379 return shim.Error(err.Error()) 380 } 381 args := append([][]byte{[]byte(funcName)}, fargs...) 382 return stub.InvokeBytes(args...) 383 } 384 385 // InvokeBytes mock invoke with autogenerated tx uuid 386 func (stub *MockStub) InvokeBytes(args ...[]byte) peer.Response { 387 return stub.MockInvoke(stub.generateTxUID(), args) 388 } 389 390 // QueryBytes mock query with autogenerated tx uuid 391 func (stub *MockStub) QueryBytes(args ...[]byte) peer.Response { 392 return stub.MockQuery(stub.generateTxUID(), args) 393 } 394 395 func (stub *MockStub) Query(funcName string, iargs ...interface{}) peer.Response { 396 return stub.Invoke(funcName, iargs...) 397 } 398 399 // GetCreator mocked 400 func (stub *MockStub) GetCreator() ([]byte, error) { 401 return stub.mockCreator, nil 402 } 403 404 // From mock tx creator 405 func (stub *MockStub) From(txCreator ...interface{}) *MockStub { 406 407 var mspID string 408 var certPEM []byte 409 var err error 410 411 if stub.creatorTransformer != nil { 412 mspID, certPEM, err = stub.creatorTransformer(txCreator...) 413 } else { 414 mspID, certPEM, err = TransformCreator(txCreator...) 415 } 416 417 if err != nil { 418 panic(err) 419 } 420 stub.MockCreator(mspID, certPEM) 421 return stub 422 } 423 424 func (stub *MockStub) GetTransient() (map[string][]byte, error) { 425 return stub.transient, nil 426 } 427 428 // WithTransient sets transient map 429 func (stub *MockStub) WithTransient(transient map[string][]byte) *MockStub { 430 stub.transient = transient 431 return stub 432 } 433 434 // AddTransient adds key-value pairs to transient map 435 func (stub *MockStub) AddTransient(transient map[string][]byte) *MockStub { 436 if stub.transient == nil { 437 stub.transient = make(map[string][]byte) 438 } 439 for k, v := range transient { 440 if _, ok := stub.transient[k]; ok { 441 panic(ErrKeyAlreadyExistsInTransientMap) 442 } 443 stub.transient[k] = v 444 } 445 return stub 446 } 447 448 // At mock tx timestamp 449 //func (stub *MockStub) At(txTimestamp *timestamp.Timestamp) *MockStub { 450 // stub.TxTimestamp = txTimestamp 451 // return stub 452 //} 453 454 // DelPrivateData mocked 455 func (stub *MockStub) DelPrivateData(collection string, key string) error { 456 m, in := stub.PvtState[collection] 457 if !in { 458 return errors.Errorf("Collection %s not found.", collection) 459 } 460 461 if _, ok := m[key]; !ok { 462 return errors.Errorf("Key %s not found.", key) 463 } 464 delete(m, key) 465 466 for elem := stub.PrivateKeys[collection].Front(); elem != nil; elem = elem.Next() { 467 if strings.Compare(key, elem.Value.(string)) == 0 { 468 stub.PrivateKeys[collection].Remove(elem) 469 } 470 } 471 return nil 472 } 473 474 type PrivateMockStateRangeQueryIterator struct { 475 Closed bool 476 Stub *MockStub 477 StartKey string 478 EndKey string 479 Current *list.Element 480 Collection string 481 } 482 483 // HasNext returns true if the range query iterator contains additional keys 484 // and values. 485 func (iter *PrivateMockStateRangeQueryIterator) HasNext() bool { 486 if iter.Closed { 487 // previously called Close() 488 return false 489 } 490 491 if iter.Current == nil { 492 return false 493 } 494 495 current := iter.Current 496 for current != nil { 497 // if this is an open-ended query for all keys, return true 498 if iter.StartKey == "" && iter.EndKey == "" { 499 return true 500 } 501 comp1 := strings.Compare(current.Value.(string), iter.StartKey) 502 comp2 := strings.Compare(current.Value.(string), iter.EndKey) 503 if comp1 >= 0 { 504 if comp2 < 0 { 505 return true 506 } else { 507 return false 508 509 } 510 } 511 current = current.Next() 512 } 513 514 // we've reached the end of the underlying values 515 return false 516 } 517 518 // Next returns the next key and value in the range query iterator. 519 func (iter *PrivateMockStateRangeQueryIterator) Next() (*queryresult.KV, error) { 520 if iter.Closed { 521 err := errors.New("PrivateMockStateRangeQueryIterator.Next() called after Close()") 522 return nil, err 523 } 524 525 if !iter.HasNext() { 526 err := errors.New("PrivateMockStateRangeQueryIterator.Next() called when it does not HaveNext()") 527 return nil, err 528 } 529 530 for iter.Current != nil { 531 comp1 := strings.Compare(iter.Current.Value.(string), iter.StartKey) 532 comp2 := strings.Compare(iter.Current.Value.(string), iter.EndKey) 533 // compare to start and end keys. or, if this is an open-ended query for 534 // all keys, it should always return the key and value 535 if (comp1 >= 0 && comp2 < 0) || (iter.StartKey == "" && iter.EndKey == "") { 536 key := iter.Current.Value.(string) 537 value, err := iter.Stub.GetPrivateData(iter.Collection, key) 538 iter.Current = iter.Current.Next() 539 return &queryresult.KV{Key: key, Value: value}, err 540 } 541 iter.Current = iter.Current.Next() 542 } 543 return nil, errors.New("PrivateMockStateRangeQueryIterator.Next() went past end of range") 544 } 545 546 // Close closes the range query iterator. This should be called when done 547 // reading from the iterator to free up resources. 548 func (iter *PrivateMockStateRangeQueryIterator) Close() error { 549 if iter.Closed { 550 return errors.New("PrivateMockStateRangeQueryIterator.Close() called after Close()") 551 } 552 553 iter.Closed = true 554 return nil 555 } 556 557 func NewPrivateMockStateRangeQueryIterator(stub *MockStub, collection string, startKey string, endKey string) *PrivateMockStateRangeQueryIterator { 558 559 if _, ok := stub.PrivateKeys[collection]; !ok { 560 stub.PrivateKeys[collection] = list.New() 561 } 562 iter := new(PrivateMockStateRangeQueryIterator) 563 iter.Closed = false 564 iter.Stub = stub 565 iter.StartKey = startKey 566 iter.EndKey = endKey 567 iter.Current = stub.PrivateKeys[collection].Front() 568 iter.Collection = collection 569 570 return iter 571 } 572 573 // PutPrivateData mocked 574 func (stub *MockStub) PutPrivateData(collection string, key string, value []byte) error { 575 if _, in := stub.PvtState[collection]; !in { 576 stub.PvtState[collection] = make(map[string][]byte) 577 } 578 stub.PvtState[collection][key] = value 579 580 if _, ok := stub.PrivateKeys[collection]; !ok { 581 stub.PrivateKeys[collection] = list.New() 582 } 583 584 for elem := stub.PrivateKeys[collection].Front(); elem != nil; elem = elem.Next() { 585 elemValue := elem.Value.(string) 586 comp := strings.Compare(key, elemValue) 587 if comp < 0 { 588 // key < elem, insert it before elem 589 stub.PrivateKeys[collection].InsertBefore(key, elem) 590 break 591 } else if comp == 0 { 592 // keys exists, no need to change 593 break 594 } else { // comp > 0 595 // key > elem, keep looking unless this is the end of the list 596 if elem.Next() == nil { 597 stub.PrivateKeys[collection].PushBack(key) 598 break 599 } 600 } 601 } 602 603 // special case for empty Keys list 604 if stub.PrivateKeys[collection].Len() == 0 { 605 stub.PrivateKeys[collection].PushFront(key) 606 } 607 608 return nil 609 } 610 611 const maxUnicodeRuneValue = utf8.MaxRune 612 613 // GetPrivateDataByPartialCompositeKey mocked 614 func (stub *MockStub) GetPrivateDataByPartialCompositeKey(collection, objectType string, attributes []string) (shim.StateQueryIteratorInterface, error) { 615 partialCompositeKey, err := stub.CreateCompositeKey(objectType, attributes) 616 if err != nil { 617 return nil, err 618 } 619 return NewPrivateMockStateRangeQueryIterator(stub, collection, partialCompositeKey, partialCompositeKey+string(maxUnicodeRuneValue)), nil 620 }