go_study/fabric-main/vendor/github.com/hyperledger/fabric-chaincode-go/shimtest/mockstub.go

637 lines
19 KiB
Go

// Copyright the Hyperledger Fabric contributors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
// Package shimtest provides a mock of the ChaincodeStubInterface for
// unit testing chaincode.
//
// Deprecated: ShimTest will be removed in a future release.
// Future development should make use of the ChaincodeStub Interface
// for generating mocks
package shimtest
import (
"container/list"
"errors"
"fmt"
"strings"
"unicode/utf8"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes"
"github.com/golang/protobuf/ptypes/timestamp"
"github.com/hyperledger/fabric-chaincode-go/shim"
"github.com/hyperledger/fabric-protos-go/ledger/queryresult"
pb "github.com/hyperledger/fabric-protos-go/peer"
)
const (
minUnicodeRuneValue = 0 //U+0000
compositeKeyNamespace = "\x00"
)
// MockStub is an implementation of ChaincodeStubInterface for unit testing chaincode.
// Use this instead of ChaincodeStub in your chaincode's unit test calls to Init or Invoke.
type MockStub struct {
// arguments the stub was called with
args [][]byte
// transientMap
TransientMap map[string][]byte
// A pointer back to the chaincode that will invoke this, set by constructor.
// If a peer calls this stub, the chaincode will be invoked from here.
cc shim.Chaincode
// A nice name that can be used for logging
Name string
// State keeps name value pairs
State map[string][]byte
// Keys stores the list of mapped values in lexical order
Keys *list.List
// registered list of other MockStub chaincodes that can be called from this MockStub
Invokables map[string]*MockStub
// stores a transaction uuid while being Invoked / Deployed
// TODO if a chaincode uses recursion this may need to be a stack of TxIDs or possibly a reference counting map
TxID string
TxTimestamp *timestamp.Timestamp
// mocked signedProposal
signedProposal *pb.SignedProposal
// stores a channel ID of the proposal
ChannelID string
PvtState map[string]map[string][]byte
// stores per-key endorsement policy, first map index is the collection, second map index is the key
EndorsementPolicies map[string]map[string][]byte
// channel to store ChaincodeEvents
ChaincodeEventsChannel chan *pb.ChaincodeEvent
Creator []byte
Decorations map[string][]byte
}
// GetTxID ...
func (stub *MockStub) GetTxID() string {
return stub.TxID
}
// GetChannelID ...
func (stub *MockStub) GetChannelID() string {
return stub.ChannelID
}
// GetArgs ...
func (stub *MockStub) GetArgs() [][]byte {
return stub.args
}
// GetStringArgs ...
func (stub *MockStub) GetStringArgs() []string {
args := stub.GetArgs()
strargs := make([]string, 0, len(args))
for _, barg := range args {
strargs = append(strargs, string(barg))
}
return strargs
}
// GetFunctionAndParameters ...
func (stub *MockStub) GetFunctionAndParameters() (function string, params []string) {
allargs := stub.GetStringArgs()
function = ""
params = []string{}
if len(allargs) >= 1 {
function = allargs[0]
params = allargs[1:]
}
return
}
// MockTransactionStart Used to indicate to a chaincode that it is part of a transaction.
// This is important when chaincodes invoke each other.
// MockStub doesn't support concurrent transactions at present.
func (stub *MockStub) MockTransactionStart(txid string) {
stub.TxID = txid
stub.setSignedProposal(&pb.SignedProposal{})
stub.setTxTimestamp(ptypes.TimestampNow())
}
// MockTransactionEnd End a mocked transaction, clearing the UUID.
func (stub *MockStub) MockTransactionEnd(uuid string) {
stub.signedProposal = nil
stub.TxID = ""
}
// MockPeerChaincode Register another MockStub chaincode with this MockStub.
// invokableChaincodeName is the name of a chaincode.
// otherStub is a MockStub of the chaincode, already initialized.
// channel is the name of a channel on which another MockStub is called.
func (stub *MockStub) MockPeerChaincode(invokableChaincodeName string, otherStub *MockStub, channel string) {
// Internally we use chaincode name as a composite name
if channel != "" {
invokableChaincodeName = invokableChaincodeName + "/" + channel
}
stub.Invokables[invokableChaincodeName] = otherStub
}
// MockInit Initialise this chaincode, also starts and ends a transaction.
func (stub *MockStub) MockInit(uuid string, args [][]byte) pb.Response {
stub.args = args
stub.MockTransactionStart(uuid)
res := stub.cc.Init(stub)
stub.MockTransactionEnd(uuid)
return res
}
// MockInvoke Invoke this chaincode, also starts and ends a transaction.
func (stub *MockStub) MockInvoke(uuid string, args [][]byte) pb.Response {
stub.args = args
stub.MockTransactionStart(uuid)
res := stub.cc.Invoke(stub)
stub.MockTransactionEnd(uuid)
return res
}
// GetDecorations ...
func (stub *MockStub) GetDecorations() map[string][]byte {
return stub.Decorations
}
// MockInvokeWithSignedProposal Invoke this chaincode, also starts and ends a transaction.
func (stub *MockStub) MockInvokeWithSignedProposal(uuid string, args [][]byte, sp *pb.SignedProposal) pb.Response {
stub.args = args
stub.MockTransactionStart(uuid)
stub.signedProposal = sp
res := stub.cc.Invoke(stub)
stub.MockTransactionEnd(uuid)
return res
}
// GetPrivateData ...
func (stub *MockStub) GetPrivateData(collection string, key string) ([]byte, error) {
m, in := stub.PvtState[collection]
if !in {
return nil, nil
}
return m[key], nil
}
// GetPrivateDataHash ...
func (stub *MockStub) GetPrivateDataHash(collection, key string) ([]byte, error) {
return nil, errors.New("Not Implemented")
}
// PutPrivateData ...
func (stub *MockStub) PutPrivateData(collection string, key string, value []byte) error {
m, in := stub.PvtState[collection]
if !in {
stub.PvtState[collection] = make(map[string][]byte)
m, in = stub.PvtState[collection]
}
m[key] = value
return nil
}
// DelPrivateData ...
func (stub *MockStub) DelPrivateData(collection string, key string) error {
return errors.New("Not Implemented")
}
// PurgePrivateData ...
func (stub *MockStub) PurgePrivateData(collection string, key string) error {
return errors.New("Not Implemented")
}
// GetPrivateDataByRange ...
func (stub *MockStub) GetPrivateDataByRange(collection, startKey, endKey string) (shim.StateQueryIteratorInterface, error) {
return nil, errors.New("Not Implemented")
}
// GetPrivateDataByPartialCompositeKey ...
func (stub *MockStub) GetPrivateDataByPartialCompositeKey(collection, objectType string, attributes []string) (shim.StateQueryIteratorInterface, error) {
return nil, errors.New("Not Implemented")
}
// GetPrivateDataQueryResult ...
func (stub *MockStub) GetPrivateDataQueryResult(collection, query string) (shim.StateQueryIteratorInterface, error) {
// Not implemented since the mock engine does not have a query engine.
// However, a very simple query engine that supports string matching
// could be implemented to test that the framework supports queries
return nil, errors.New("Not Implemented")
}
// GetState retrieves the value for a given key from the ledger
func (stub *MockStub) GetState(key string) ([]byte, error) {
value := stub.State[key]
return value, nil
}
// PutState writes the specified `value` and `key` into the ledger.
func (stub *MockStub) PutState(key string, value []byte) error {
if stub.TxID == "" {
err := errors.New("cannot PutState without a transactions - call stub.MockTransactionStart()?")
return err
}
// If the value is nil or empty, delete the key
if len(value) == 0 {
return stub.DelState(key)
}
stub.State[key] = value
// insert key into ordered list of keys
for elem := stub.Keys.Front(); elem != nil; elem = elem.Next() {
elemValue := elem.Value.(string)
comp := strings.Compare(key, elemValue)
if comp < 0 {
// key < elem, insert it before elem
stub.Keys.InsertBefore(key, elem)
break
} else if comp == 0 {
// keys exists, no need to change
break
} else { // comp > 0
// key > elem, keep looking unless this is the end of the list
if elem.Next() == nil {
stub.Keys.PushBack(key)
break
}
}
}
// special case for empty Keys list
if stub.Keys.Len() == 0 {
stub.Keys.PushFront(key)
}
return nil
}
// DelState removes the specified `key` and its value from the ledger.
func (stub *MockStub) DelState(key string) error {
delete(stub.State, key)
for elem := stub.Keys.Front(); elem != nil; elem = elem.Next() {
if strings.Compare(key, elem.Value.(string)) == 0 {
stub.Keys.Remove(elem)
}
}
return nil
}
// GetStateByRange ...
func (stub *MockStub) GetStateByRange(startKey, endKey string) (shim.StateQueryIteratorInterface, error) {
if err := validateSimpleKeys(startKey, endKey); err != nil {
return nil, err
}
return NewMockStateRangeQueryIterator(stub, startKey, endKey), nil
}
//To ensure that simple keys do not go into composite key namespace,
//we validate simplekey to check whether the key starts with 0x00 (which
//is the namespace for compositeKey). This helps in avoding simple/composite
//key collisions.
func validateSimpleKeys(simpleKeys ...string) error {
for _, key := range simpleKeys {
if len(key) > 0 && key[0] == compositeKeyNamespace[0] {
return fmt.Errorf(`first character of the key [%s] contains a null character which is not allowed`, key)
}
}
return nil
}
// GetQueryResult function can be invoked by a chaincode to perform a
// rich query against state database. Only supported by state database implementations
// that support rich query. The query string is in the syntax of the underlying
// state database. An iterator is returned which can be used to iterate (next) over
// the query result set
func (stub *MockStub) GetQueryResult(query string) (shim.StateQueryIteratorInterface, error) {
// Not implemented since the mock engine does not have a query engine.
// However, a very simple query engine that supports string matching
// could be implemented to test that the framework supports queries
return nil, errors.New("not implemented")
}
// GetHistoryForKey function can be invoked by a chaincode to return a history of
// key values across time. GetHistoryForKey is intended to be used for read-only queries.
func (stub *MockStub) GetHistoryForKey(key string) (shim.HistoryQueryIteratorInterface, error) {
return nil, errors.New("not implemented")
}
// GetStateByPartialCompositeKey function can be invoked by a chaincode to query the
// state based on a given partial composite key. This function returns an
// iterator which can be used to iterate over all composite keys whose prefix
// matches the given partial composite key. This function should be used only for
// a partial composite key. For a full composite key, an iter with empty response
// would be returned.
func (stub *MockStub) GetStateByPartialCompositeKey(objectType string, attributes []string) (shim.StateQueryIteratorInterface, error) {
partialCompositeKey, err := stub.CreateCompositeKey(objectType, attributes)
if err != nil {
return nil, err
}
return NewMockStateRangeQueryIterator(stub, partialCompositeKey, partialCompositeKey+string(utf8.MaxRune)), nil
}
// CreateCompositeKey combines the list of attributes
// to form a composite key.
func (stub *MockStub) CreateCompositeKey(objectType string, attributes []string) (string, error) {
return shim.CreateCompositeKey(objectType, attributes)
}
// SplitCompositeKey splits the composite key into attributes
// on which the composite key was formed.
func (stub *MockStub) SplitCompositeKey(compositeKey string) (string, []string, error) {
return splitCompositeKey(compositeKey)
}
func splitCompositeKey(compositeKey string) (string, []string, error) {
componentIndex := 1
components := []string{}
for i := 1; i < len(compositeKey); i++ {
if compositeKey[i] == minUnicodeRuneValue {
components = append(components, compositeKey[componentIndex:i])
componentIndex = i + 1
}
}
return components[0], components[1:], nil
}
// GetStateByRangeWithPagination ...
func (stub *MockStub) GetStateByRangeWithPagination(startKey, endKey string, pageSize int32,
bookmark string) (shim.StateQueryIteratorInterface, *pb.QueryResponseMetadata, error) {
return nil, nil, nil
}
// GetStateByPartialCompositeKeyWithPagination ...
func (stub *MockStub) GetStateByPartialCompositeKeyWithPagination(objectType string, keys []string,
pageSize int32, bookmark string) (shim.StateQueryIteratorInterface, *pb.QueryResponseMetadata, error) {
return nil, nil, nil
}
// GetQueryResultWithPagination ...
func (stub *MockStub) GetQueryResultWithPagination(query string, pageSize int32,
bookmark string) (shim.StateQueryIteratorInterface, *pb.QueryResponseMetadata, error) {
return nil, nil, nil
}
// InvokeChaincode locally calls the specified chaincode `Invoke`.
// E.g. stub1.InvokeChaincode("othercc", funcArgs, channel)
// Before calling this make sure to create another MockStub stub2, call shim.NewMockStub("othercc", Chaincode)
// and register it with stub1 by calling stub1.MockPeerChaincode("othercc", stub2, channel)
func (stub *MockStub) InvokeChaincode(chaincodeName string, args [][]byte, channel string) pb.Response {
// Internally we use chaincode name as a composite name
if channel != "" {
chaincodeName = chaincodeName + "/" + channel
}
// TODO "args" here should possibly be a serialized pb.ChaincodeInput
otherStub := stub.Invokables[chaincodeName]
// function, strings := getFuncArgs(args)
res := otherStub.MockInvoke(stub.TxID, args)
return res
}
// GetCreator ...
func (stub *MockStub) GetCreator() ([]byte, error) {
return stub.Creator, nil
}
// SetTransient set TransientMap to mockStub
func (stub *MockStub) SetTransient(tMap map[string][]byte) error {
if stub.signedProposal == nil {
return fmt.Errorf("signedProposal is not initialized")
}
payloadByte, err := proto.Marshal(&pb.ChaincodeProposalPayload{
TransientMap: tMap,
})
if err != nil {
return err
}
proposalByte, err := proto.Marshal(&pb.Proposal{
Payload: payloadByte,
})
if err != nil {
return err
}
stub.signedProposal.ProposalBytes = proposalByte
stub.TransientMap = tMap
return nil
}
// GetTransient ...
func (stub *MockStub) GetTransient() (map[string][]byte, error) {
return stub.TransientMap, nil
}
// GetBinding Not implemented ...
func (stub *MockStub) GetBinding() ([]byte, error) {
return nil, nil
}
// GetSignedProposal Not implemented ...
func (stub *MockStub) GetSignedProposal() (*pb.SignedProposal, error) {
return stub.signedProposal, nil
}
func (stub *MockStub) setSignedProposal(sp *pb.SignedProposal) {
stub.signedProposal = sp
}
// GetArgsSlice Not implemented ...
func (stub *MockStub) GetArgsSlice() ([]byte, error) {
return nil, nil
}
func (stub *MockStub) setTxTimestamp(time *timestamp.Timestamp) {
stub.TxTimestamp = time
}
// GetTxTimestamp ...
func (stub *MockStub) GetTxTimestamp() (*timestamp.Timestamp, error) {
if stub.TxTimestamp == nil {
return nil, errors.New("TxTimestamp not set")
}
return stub.TxTimestamp, nil
}
// SetEvent ...
func (stub *MockStub) SetEvent(name string, payload []byte) error {
stub.ChaincodeEventsChannel <- &pb.ChaincodeEvent{EventName: name, Payload: payload}
return nil
}
// SetStateValidationParameter ...
func (stub *MockStub) SetStateValidationParameter(key string, ep []byte) error {
return stub.SetPrivateDataValidationParameter("", key, ep)
}
// GetStateValidationParameter ...
func (stub *MockStub) GetStateValidationParameter(key string) ([]byte, error) {
return stub.GetPrivateDataValidationParameter("", key)
}
// SetPrivateDataValidationParameter ...
func (stub *MockStub) SetPrivateDataValidationParameter(collection, key string, ep []byte) error {
m, in := stub.EndorsementPolicies[collection]
if !in {
stub.EndorsementPolicies[collection] = make(map[string][]byte)
m, in = stub.EndorsementPolicies[collection]
}
m[key] = ep
return nil
}
// GetPrivateDataValidationParameter ...
func (stub *MockStub) GetPrivateDataValidationParameter(collection, key string) ([]byte, error) {
m, in := stub.EndorsementPolicies[collection]
if !in {
return nil, nil
}
return m[key], nil
}
// NewMockStub Constructor to initialise the internal State map
func NewMockStub(name string, cc shim.Chaincode) *MockStub {
s := new(MockStub)
s.Name = name
s.cc = cc
s.State = make(map[string][]byte)
s.PvtState = make(map[string]map[string][]byte)
s.EndorsementPolicies = make(map[string]map[string][]byte)
s.Invokables = make(map[string]*MockStub)
s.Keys = list.New()
s.ChaincodeEventsChannel = make(chan *pb.ChaincodeEvent, 100) //define large capacity for non-blocking setEvent calls.
s.Decorations = make(map[string][]byte)
return s
}
/*****************************
Range Query Iterator
*****************************/
// MockStateRangeQueryIterator ...
type MockStateRangeQueryIterator struct {
Closed bool
Stub *MockStub
StartKey string
EndKey string
Current *list.Element
}
// HasNext returns true if the range query iterator contains additional keys
// and values.
func (iter *MockStateRangeQueryIterator) HasNext() bool {
if iter.Closed {
// previously called Close()
return false
}
if iter.Current == nil {
return false
}
current := iter.Current
for current != nil {
// if this is an open-ended query for all keys, return true
if iter.StartKey == "" && iter.EndKey == "" {
return true
}
comp1 := strings.Compare(current.Value.(string), iter.StartKey)
comp2 := strings.Compare(current.Value.(string), iter.EndKey)
if comp1 >= 0 {
if comp2 < 0 {
return true
}
return false
}
current = current.Next()
}
return false
}
// Next returns the next key and value in the range query iterator.
func (iter *MockStateRangeQueryIterator) Next() (*queryresult.KV, error) {
if iter.Closed == true {
err := errors.New("MockStateRangeQueryIterator.Next() called after Close()")
return nil, err
}
if iter.HasNext() == false {
err := errors.New("MockStateRangeQueryIterator.Next() called when it does not HaveNext()")
return nil, err
}
for iter.Current != nil {
comp1 := strings.Compare(iter.Current.Value.(string), iter.StartKey)
comp2 := strings.Compare(iter.Current.Value.(string), iter.EndKey)
// compare to start and end keys. or, if this is an open-ended query for
// all keys, it should always return the key and value
if (comp1 >= 0 && comp2 < 0) || (iter.StartKey == "" && iter.EndKey == "") {
key := iter.Current.Value.(string)
value, err := iter.Stub.GetState(key)
iter.Current = iter.Current.Next()
return &queryresult.KV{Key: key, Value: value}, err
}
iter.Current = iter.Current.Next()
}
err := errors.New("MockStateRangeQueryIterator.Next() went past end of range")
return nil, err
}
// Close closes the range query iterator. This should be called when done
// reading from the iterator to free up resources.
func (iter *MockStateRangeQueryIterator) Close() error {
if iter.Closed == true {
err := errors.New("MockStateRangeQueryIterator.Close() called after Close()")
return err
}
iter.Closed = true
return nil
}
// NewMockStateRangeQueryIterator ...
func NewMockStateRangeQueryIterator(stub *MockStub, startKey string, endKey string) *MockStateRangeQueryIterator {
iter := new(MockStateRangeQueryIterator)
iter.Closed = false
iter.Stub = stub
iter.StartKey = startKey
iter.EndKey = endKey
iter.Current = stub.Keys.Front()
return iter
}
func getBytes(function string, args []string) [][]byte {
bytes := make([][]byte, 0, len(args)+1)
bytes = append(bytes, []byte(function))
for _, s := range args {
bytes = append(bytes, []byte(s))
}
return bytes
}
func getFuncArgs(bytes [][]byte) (string, []string) {
function := string(bytes[0])
args := make([]string, len(bytes)-1)
for i := 1; i < len(bytes); i++ {
args[i-1] = string(bytes[i])
}
return function, args
}