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  }